home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Text / SimpleText Sample / TextFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-17  |  86.8 KB  |  3,137 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        TextFile.c
  3.  
  4.     Contains:    Text file support for simple text application.
  5.  
  6.     Version:    SimpleText 1.4 or later
  7.  
  8. ** Copyright 1993-1996 Apple Computer. All rights reserved.
  9. **
  10. **    You may incorporate this sample code into your applications without
  11. **    restriction, though the sample code has been provided "AS IS" and the
  12. **    responsibility for its operation is 100% yours.  However, what you are
  13. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  14. **    after having made changes. If you're going to re-distribute the source,
  15. **    we require that you make it clear in the source that the code was
  16. **    descended from Apple Sample Code, but that you've made changes.
  17.  
  18. */
  19.  
  20. #include "MacIncludes.h"
  21.  
  22. #include "TextFile.h"
  23.  
  24. #pragma segment Text
  25.  
  26.  
  27.  
  28. // --------------------------------------------------------------------------------------------------------------
  29. // INTERNAL DEFINES
  30. // --------------------------------------------------------------------------------------------------------------
  31. #define kOnePageWidth         600            // preferred width of a window
  32. #define kMargins            4            // margins in window
  33. #define kPrintMargins        8            // margins in printing window
  34. #define kGXPrintMargins        10            // margins in printing a GX window
  35.  
  36. #define kPictureBase        1000        // resource base ID for PICTs in documents
  37. #define kSoundBase            10000        // resource base ID for 'snd 's in documents
  38.  
  39. // resources for controling printing
  40. #define kFormResource        'form'
  41.     #define kFormFeed            0x00000001    // form feed after this line
  42.  
  43. #define    kContentsListID        10000        // resource ID for STR# contents menu         
  44.  
  45. // some memory requirements
  46. #define kPrefBufferSize        90*1024
  47. #define kMinBufferSize        5*1024
  48.  
  49. #define kDeleteKey            8
  50. #define kForwardDeleteKey    0x75
  51.  
  52.  
  53. extern pascal void AsmClikLoop();
  54.  
  55. #define ABS(n)                (((n) < 0) ? -(n) : (n))
  56.  
  57. // --------------------------------------------------------------------------------------------------------------
  58. // GLOBALS USED ONLY BY THESE ROUTINES
  59. // --------------------------------------------------------------------------------------------------------------
  60.  
  61. // These variables are used for speech, notice that on purpose we only
  62. // support a single channel at a time.
  63. static SpeechChannel    gSpeechChannel = nil;        
  64. static VoiceSpec        gCurrentVoice;
  65. static Ptr                gSpeakPtr = nil;
  66. static Boolean            gAddedVoices = false;
  67. static Str31            gPictMarker1, gPictMarker2;
  68.  
  69. // --------------------------------------------------------------------------------------------------------------
  70. // EXTERNAL FUNCTIONS
  71. // --------------------------------------------------------------------------------------------------------------
  72.  
  73. extern OSErr    TextDragTracking(WindowRef pWindow, void *pData, DragReference theDragRef, short message);
  74. extern OSErr    TextDragReceive(WindowRef pWindow, void *refCon, DragReference theDragRef);
  75. extern Boolean    DragText(WindowRef pWindow, void *pData, EventRecord *pEvent, RgnHandle hilightRgn);
  76.  
  77. // --------------------------------------------------------------------------------------------------------------
  78. // INTERNAL ROUTINES
  79. // --------------------------------------------------------------------------------------------------------------
  80.  
  81. static void TextAddContentsMenu(WindowDataPtr pData);
  82. static void TextRemoveContentsMenu(WindowDataPtr pData);
  83. static OSErr TextGetContentsListItem(WindowDataPtr pData, short itemNum, StringPtr menuStr, StringPtr searchStr, short *totalItems);
  84. static OSErr TextAdjustContentsMenu(WindowDataPtr pData);
  85.  
  86. // --------------------------------------------------------------------------------------------------------------
  87. // UNDO UTILITY FUNCTIONS
  88. // --------------------------------------------------------------------------------------------------------------
  89. OSErr SaveCurrentUndoState(WindowDataPtr pData, short newCommandID)
  90. {
  91.     OSErr        anErr = noErr;
  92.     TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  93.     
  94.     // this is only a new command if:
  95.     //    the command ID is different
  96.     // OR
  97.     //    the selection is a range, and not equal to the old one
  98.     if     (
  99.             ( ((TextDataPtr) pData)->prevCommandID != newCommandID )
  100.         ||
  101.             (
  102.                 ( (**hTE).selStart != (**hTE).selEnd )
  103.             ||
  104.                 ( ABS((**hTE).selStart - ((TextDataPtr) pData)->prevSelStart) > 1 )
  105.             )
  106.         )
  107.         {
  108.         Handle        tempHandle;
  109.         Size        tempSize;
  110.         
  111.         // if we don't have save handles, make em!
  112.         if (!((TextDataPtr) pData)->prevText)
  113.             {
  114.             ((TextDataPtr) pData)->prevText = NewHandle(0);
  115.             anErr = MemError();
  116.             nrequire(anErr, MakeTextSave);
  117.             }
  118.         if  (!((TextDataPtr) pData)->prevStyle) 
  119.             {
  120.             ((TextDataPtr) pData)->prevStyle = NewHandle(0);
  121.             anErr = MemError();
  122.             nrequire(anErr, MakeStyleSave);
  123.             }
  124.             
  125.         // grow the save handles and block move into them
  126.         tempHandle = (**hTE).hText;
  127.         tempSize = GetHandleSize(tempHandle);
  128.         SetHandleSize( ((TextDataPtr) pData)->prevText, tempSize);
  129.         anErr = MemError();
  130.         nrequire(anErr, GrowTextSave);
  131.         BlockMoveData(*tempHandle, * ((TextDataPtr) pData)->prevText, tempSize );
  132.  
  133.         tempHandle = (Handle) TEGetStyleHandle(hTE);
  134.         tempSize = GetHandleSize(tempHandle);
  135.         SetHandleSize( ((TextDataPtr) pData)->prevStyle, tempSize);
  136.         anErr = MemError();
  137.         nrequire(anErr, GrowTextSave);
  138.         BlockMoveData(*tempHandle, * ((TextDataPtr) pData)->prevStyle, tempSize );
  139.  
  140.         // save length
  141.         ((TextDataPtr) pData)->prevLength = (**hTE).teLength;
  142.         ((TextDataPtr) pData)->beforeSelStart = (**hTE).selStart;
  143.         ((TextDataPtr) pData)->beforeSelEnd = (**hTE).selEnd;
  144.         }
  145.     
  146.     // save start and end of the selection
  147.     ((TextDataPtr) pData)->prevSelStart = (**hTE).selStart;
  148.  
  149.     // if we didn't have any problems, then we can undo this operation
  150.     ((TextDataPtr) pData)->prevCommandID = newCommandID;
  151.     return(noErr);
  152.     
  153. // EXCEPTION HANDLING
  154. GrowStyleSave:
  155. GrowTextSave:
  156. MakeStyleSave:
  157. MakeTextSave:
  158.     // can't undo because of an error 
  159.     // (at some point might warn the user, but that's probably more annoying)
  160.     ((TextDataPtr) pData)->prevCommandID = cNull;
  161.     
  162.     return(anErr);
  163.     
  164. } // SaveCurrentUndoState
  165.  
  166. // --------------------------------------------------------------------------------------------------------------
  167. static void PerformUndo(WindowDataPtr pData)
  168. {
  169.     if (((TextDataPtr) pData)->prevCommandID != cNull)
  170.         {
  171.         TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  172.         long        tempLong;
  173.         
  174.         // undo text
  175.         tempLong = (long) (**hTE).hText;
  176.         (**hTE).hText = ((TextDataPtr) pData)->prevText;
  177.         ((TextDataPtr) pData)->prevText = (Handle)tempLong;
  178.  
  179.         // undo length
  180.         tempLong = (long) (**hTE).teLength;
  181.         (**hTE).teLength = ((TextDataPtr) pData)->prevLength;
  182.         ((TextDataPtr) pData)->prevLength = tempLong;
  183.  
  184.         // undo style
  185.         tempLong = (long) TEGetStyleHandle(hTE);
  186.         if (HandToHand((Handle*) &tempLong) == noErr)
  187.             {
  188.             TESetStyleHandle( (TEStyleHandle) ((TextDataPtr) pData)->prevStyle, hTE );
  189.             ((TextDataPtr) pData)->prevStyle = (Handle)tempLong;
  190.             }
  191.             
  192.         // undo selection
  193.             {
  194.             short    start, end;
  195.             
  196.             start = ((TextDataPtr) pData)->beforeSelStart;
  197.             end = ((TextDataPtr) pData)->beforeSelEnd;
  198.             ((TextDataPtr) pData)->prevSelStart = (**hTE).selStart;
  199.             TESetSelect(start, end, hTE);
  200.             }
  201.         
  202.         }
  203.         
  204. } // PerformUndo
  205.  
  206.  
  207. // --------------------------------------------------------------------------------------------------------------
  208. // TEXT EDIT UTILITY FUNCTIONS
  209. // --------------------------------------------------------------------------------------------------------------
  210. static long CalculateTextEditHeight(TEHandle hTE)
  211. {
  212.     long    result;
  213.     short    length;
  214.     
  215.     result = TEGetHeight(32767, 0, hTE);
  216.     length = (**hTE).teLength;
  217.     
  218.     // Text Edit doesn't return the height of the last character, if that
  219.     // character is a <cr>.  So if we see that, we go grab the height of
  220.     // that last character and add it into the total height.
  221.     if ( (length) && ( (*(**hTE).hText)[length-1] == '\n') )
  222.         {
  223.         TextStyle    theStyle;
  224.         short        theHeight;
  225.         short        theAscent;
  226.  
  227.         TEGetStyle(length, &theStyle, &theHeight, &theAscent, hTE);
  228.         result += theHeight;
  229.         }
  230.  
  231.     return result;
  232.  
  233. } // CalculateTextEditHeight
  234.  
  235. // --------------------------------------------------------------------------------------------------------------
  236. void AdjustTE(WindowDataPtr pData, Boolean doScroll)
  237. {
  238.     TEPtr        te;
  239.     short        value;
  240.     short        prevValue;
  241.     
  242.     te = *((TextDataPtr) pData)->hTE;
  243.     prevValue = GetControlValue(pData->vScroll);
  244.     value = te->viewRect.top - te->destRect.top;
  245.     SetControlValue(pData->vScroll, value);
  246.  
  247.     te = *((TextDataPtr) pData)->hTE;
  248.     if (doScroll)
  249.         {
  250.         short    hScroll = te->viewRect.left - te->destRect.left;
  251.         short    vScroll = value - prevValue;
  252.         if ((hScroll != 0) || (vScroll != 0))
  253.             TEScroll(hScroll, vScroll, ((TextDataPtr) pData)->hTE);
  254.         }
  255.     
  256.             
  257. } // AdjustTE
  258.  
  259. // --------------------------------------------------------------------------------------------------------------
  260.  
  261. static void RecalcTE(WindowDataPtr pData, Boolean doInval)
  262. {
  263.     Rect    viewRect, destRect;
  264.     short    oldOffset;
  265.     
  266.     destRect = (**((TextDataPtr) pData)->hTE).destRect;
  267.     viewRect = (**((TextDataPtr) pData)->hTE).viewRect;
  268.     oldOffset = destRect.top - viewRect.top;
  269.     
  270.     viewRect = pData->contentRect;
  271.             
  272.     InsetRect(&viewRect, kMargins, kMargins);
  273.     destRect = viewRect;
  274.     
  275.     OffsetRect(&destRect, 0, oldOffset);
  276.     
  277.     (**((TextDataPtr) pData)->hTE).viewRect = viewRect;
  278.     (**((TextDataPtr) pData)->hTE).destRect = destRect;
  279.     
  280.     TECalText(((TextDataPtr) pData)->hTE);
  281.  
  282.     if (doInval)
  283.         InvalRect(&qd.thePort->portRect);
  284.     
  285. } // RecalcTE
  286.  
  287. // --------------------------------------------------------------------------------------------------------------
  288.  
  289. pascal TEClickLoopUPP GetOldClickLoop(void)
  290. {
  291.     return ((TextDataPtr) FrontWindow())->docClick;
  292.     
  293. } // GetOldClikLoop
  294.  
  295. pascal void TextClickLoop(void)
  296. {    
  297.     WindowRef    window;
  298.     RgnHandle    region;
  299.     
  300.     window = FrontWindow();
  301.     region = NewRgn();
  302.     GetClip(region);                    /* save clip */
  303.     ClipRect(&GetWindowPort(window)->portRect);
  304.     ((TextDataPtr) window)->insideClickLoop = true;
  305.         AdjustScrollBars(window, false, false, nil);
  306.     ((TextDataPtr) window)->insideClickLoop = false;
  307.     SetClip(region);                    /* restore clip */
  308.     DisposeRgn(region);
  309.  
  310. } // TextClickLoop
  311.  
  312. #if GENERATINGPOWERPC
  313. static pascal Boolean CClikLoop(TEPtr pTE)
  314. {
  315.     CallTEClickLoopProc(GetOldClickLoop(), pTE);
  316.     
  317.     TextClickLoop();
  318.     
  319.     return(true);
  320.     
  321. } // CClikLoop
  322.  
  323.     static RoutineDescriptor gMyClickLoopRD = BUILD_ROUTINE_DESCRIPTOR(uppTEClickLoopProcInfo, CClikLoop);
  324.     static TEClickLoopUPP gMyClickLoop = &gMyClickLoopRD;
  325. #else
  326.     static TEClickLoopUPP gMyClickLoop = NewDrawHookProc(AsmClikLoop);
  327. #endif
  328.  
  329. // --------------------------------------------------------------------------------------------------------------
  330. #if GENERATINGPOWERPC
  331. pascal void MyDrawHook ( unsigned short offset, unsigned short textLen,
  332.      Ptr textPtr, TEPtr tePtr, TEHandle teHdl )
  333. #else
  334. pascal void MyDrawGlue();
  335.  
  336. pascal void MyDrawHook ( TEHandle teHdl, TEPtr tePtr, Ptr textPtr, short textLen, short offset )
  337. #endif
  338. {
  339. #pragma unused    ( teHdl, tePtr)
  340.  
  341.     register short            drawLen = 0;
  342.     register Ptr            drawPtr;
  343.     
  344.     drawPtr = textPtr + (long)offset;
  345.     
  346.     while ( textLen--)
  347.         {
  348.         if ( *drawPtr == 0xCA &&
  349.              ( *(drawPtr - 1) == 0x0D || *tePtr->hText == textPtr) )
  350.             {
  351.             DrawText( textPtr, offset, drawLen);
  352.             DrawChar( '\040');
  353.             offset += drawLen + 1;
  354.             drawLen = 0;
  355.             }
  356.         else
  357.             {
  358.             ++drawLen;
  359.             }
  360.         ++drawPtr;
  361.         }
  362.     
  363.     DrawText( textPtr, offset, drawLen);
  364.     
  365. } // MyDrawHook
  366.  
  367. #if GENERATINGCFM
  368.     static RoutineDescriptor gMyDrawGlueRD = BUILD_ROUTINE_DESCRIPTOR(uppDrawHookProcInfo, MyDrawHook);
  369.     static DrawHookUPP gMyDrawGlue = &gMyDrawGlueRD;
  370. #else
  371.     static DrawHookUPP gMyDrawGlue = NewDrawHookProc(MyDrawGlue);
  372. #endif
  373.  
  374. // --------------------------------------------------------------------------------------------------------------
  375. static void DisposeOfSpeech(Boolean throwAway)
  376. {
  377.     if (gSpeechChannel)
  378.         {
  379.         (void) StopSpeech( gSpeechChannel );
  380.         if (throwAway)
  381.             {
  382.             (void) DisposeSpeechChannel( gSpeechChannel );
  383.             gSpeechChannel = nil;
  384.             }
  385.         }
  386.     if (gSpeakPtr)
  387.         {
  388.         DisposePtr(gSpeakPtr);
  389.         gSpeakPtr = nil;
  390.         }
  391.         
  392. } // DisposeOfSpeech
  393.  
  394. // --------------------------------------------------------------------------------------------------------------
  395.  
  396. static Boolean FindNextPicture(Handle textHandle, short *offset, short *delta)
  397. {
  398.     long result;
  399.     
  400.     // marker at begining of document means a picture there
  401.     if ( (*offset == 0) && ( (*(unsigned char*)(*textHandle + (*offset))) == 0xCA) )
  402.         {
  403.         *delta = 1;
  404.         return true;
  405.         }
  406.  
  407.     if (gPictMarker1[0] != 0)
  408.         {
  409.         result = Munger(textHandle, *offset, &gPictMarker1[1], gPictMarker1[0], nil, 0);
  410.         if (result >= 0)
  411.             {
  412.             *offset = result;
  413.             *delta = gPictMarker1[0];
  414.             return true;
  415.             }
  416.         }
  417.  
  418.     if (gPictMarker2[0] != 0)
  419.         {
  420.         result = Munger(textHandle, *offset, &gPictMarker2[1], gPictMarker2[0], nil, 0);
  421.         if (result >= 0)
  422.             {
  423.             *offset = result;
  424.             *delta = gPictMarker2[0];
  425.             return true;
  426.             }
  427.         }
  428.         
  429.     *delta = 1;
  430.     return false;
  431.     
  432. } // FindNextPicture
  433.  
  434. // --------------------------------------------------------------------------------------------------------------
  435. static Boolean LineHasPageBreak(short lineNum, TEHandle hTE)
  436. {
  437.     Boolean        result = false;
  438.     short        offset, delta, lineStartOffset;
  439.     short        textLength;
  440.     Handle        textHandle;
  441.     short        pictIndex = 0;
  442.     
  443.     textHandle = (** hTE).hText;
  444.     lineStartOffset = (**hTE).lineStarts[lineNum];
  445.     textLength = (**hTE).lineStarts[lineNum+1];
  446.     
  447.     offset = 0;
  448.     while (offset < textLength)
  449.         {
  450.         if (FindNextPicture(textHandle, &offset, &delta))
  451.             {
  452.             Handle    formHandle;
  453.             
  454.             formHandle = Get1Resource(kFormResource, kFormResource + pictIndex);
  455.             if (formHandle)
  456.                 {
  457.                 long    options = **(long**)formHandle;
  458.                 
  459.                 ReleaseResource(formHandle);
  460.                 if ( (options & kFormFeed) && (offset >= lineStartOffset) && (offset < textLength) )
  461.                     {
  462.                     result = true;
  463.                     break;
  464.                     }
  465.                 }
  466.             ++pictIndex;
  467.             }
  468.         
  469.         offset += delta;
  470.         }
  471.         
  472.     return(result);
  473.     
  474. } // LineHasPageBreak
  475.  
  476. // --------------------------------------------------------------------------------------------------------------
  477. #define USE_PICT_SPOOL_CACHE 1
  478.  
  479. enum {kSpoolCacheBlockSize = 1024};        // 1K is arbitrary, perhaps could be tuned
  480.  
  481. static Handle    gSpoolPicture;
  482. static long        gSpoolOffset;
  483. static Handle    gSpoolCacheBlock;
  484. static long        gSpoolCacheOffset;
  485. static long        gSpoolCacheSize;
  486.  
  487. // --------------------------------------------------------------------------------------------------------------
  488. /*
  489.     ReadPartialResource is very painful over slow links such as ARA or ISDN. This code implements a simple
  490.     cache that vastly improves the performance of displaying embedded pictures over a slow network link.
  491.     The cache also improves scrolling performance in documents with many embedded pictures, even on local
  492.     volumes.
  493. */
  494. static short ReadPartialData(Ptr dataPtr, short byteCount)
  495. {
  496. #if USE_PICT_SPOOL_CACHE
  497.  
  498.     if (gSpoolCacheBlock == NULL)
  499.         {
  500.         gSpoolCacheBlock = NewHandle(kSpoolCacheBlockSize);
  501.         if (gSpoolCacheBlock != NULL)
  502.             {
  503.             long    cacheBytes    = GetResourceSizeOnDisk(gSpoolPicture) - gSpoolOffset;
  504.             
  505.             if (cacheBytes > kSpoolCacheBlockSize)
  506.                 cacheBytes = kSpoolCacheBlockSize;
  507.             
  508.             HLock(gSpoolCacheBlock);
  509.             ReadPartialResource(gSpoolPicture, gSpoolOffset, *gSpoolCacheBlock, cacheBytes);
  510.             HUnlock(gSpoolCacheBlock);
  511.             
  512.             gSpoolCacheOffset = gSpoolOffset;
  513.             gSpoolCacheSize = cacheBytes;
  514.             }
  515.         }
  516.     
  517.     // if the requested data is entirely present in the cache block, get it from there…
  518.     if (gSpoolCacheBlock != NULL &&
  519.         gSpoolOffset >= gSpoolCacheOffset && gSpoolOffset + byteCount <= gSpoolCacheOffset + gSpoolCacheSize)
  520.         {
  521.         BlockMoveData(*gSpoolCacheBlock + (gSpoolOffset - gSpoolCacheOffset), dataPtr, byteCount);
  522.         return byteCount;
  523.         }
  524.     // …else read it directly from disk, and force the cache block to be filled with new data
  525.     else
  526.         {
  527.         ReadPartialResource(gSpoolPicture, gSpoolOffset, dataPtr, byteCount);
  528.         if (gSpoolCacheBlock != NULL)
  529.             {
  530.             DisposeHandle(gSpoolCacheBlock);
  531.             gSpoolCacheBlock = NULL;
  532.             }
  533.         return byteCount;
  534.         }
  535.  
  536. #else
  537.  
  538.     ReadPartialResource(gSpoolPicture, gSpoolOffset, dataPtr, byteCount);
  539.     return byteCount;
  540.     
  541. #endif
  542.     
  543. } // ReadPartialData
  544.  
  545. // --------------------------------------------------------------------------------------------------------------
  546. static pascal void GetPartialPICTData(Ptr dataPtr,short byteCount)
  547. {
  548.     while (byteCount > 0)
  549.     {
  550.         short    readBytes    = ReadPartialData(dataPtr, byteCount);
  551.         
  552.         byteCount -= readBytes;
  553.         dataPtr += readBytes;
  554.         
  555.         gSpoolOffset += readBytes;
  556.     }
  557.     
  558. } // GetPartialPICTData
  559.  
  560. #if GENERATINGCFM
  561.     static RoutineDescriptor gGetPartialPICTDataRD = BUILD_ROUTINE_DESCRIPTOR(uppQDGetPicProcInfo, GetPartialPICTData);
  562.     static QDGetPicUPP gGetPartialPICTData = &gGetPartialPICTDataRD;
  563. #else
  564.     static QDGetPicUPP gGetPartialPICTData = NewQDGetPicProc(GetPartialPICTData);
  565. #endif
  566.  
  567. // --------------------------------------------------------------------------------------------------------------
  568. static void SpoolDrawPicture(Handle spoolPicture, PicHandle pictureHeader, Rect *pRect)
  569. {
  570.     CQDProcs    spoolProcs;
  571.     QDProcs        *oldProcs;
  572.  
  573.     gSpoolPicture = spoolPicture;
  574.     gSpoolOffset = sizeof(Picture);
  575.     
  576.     if (gMachineInfo.theEnvirons.hasColorQD)
  577.         SetStdCProcs(&spoolProcs);
  578.     else
  579.         SetStdProcs((QDProcs*) &spoolProcs);
  580.         
  581.     spoolProcs.getPicProc = gGetPartialPICTData;
  582.     oldProcs = qd.thePort->grafProcs;
  583.     qd.thePort->grafProcs = (QDProcs*) &spoolProcs;
  584.         DrawPicture(pictureHeader, pRect);
  585.     qd.thePort->grafProcs = oldProcs;
  586.     
  587.     if (gSpoolCacheBlock != NULL)
  588.         {
  589.         DisposeHandle(gSpoolCacheBlock);
  590.         gSpoolCacheBlock = NULL;
  591.         }
  592.     
  593. } // SpoolDrawPicture
  594.  
  595. // --------------------------------------------------------------------------------------------------------------
  596. static void DrawPictures( WindowDataPtr pData, TEHandle hTE)
  597. {
  598.     Handle    textHandle;
  599.     long    textLength;
  600.     short    oldResFile;
  601.     short    pictIndex;
  602.     short    numPicts;
  603.     
  604.     oldResFile = CurResFile();
  605.     UseResFile(pData->resRefNum);
  606.     
  607.     numPicts = Count1Resources('PICT');
  608.     pictIndex = 0;
  609.     
  610.     if (numPicts != 0)
  611.         {
  612.         short                offset, delta;
  613.         RgnHandle            oldClip;
  614.         Rect                theRect;
  615.         Rect                viewRect;
  616.         
  617.         viewRect = theRect = (** hTE).viewRect;
  618.         // intersect our viewing area with the actual clip to avoid
  619.         // drawing over the scroll bars
  620.             {
  621.             RgnHandle    newClip = NewRgn();
  622.             
  623.             oldClip = NewRgn();
  624.             GetClip(oldClip);
  625.             RectRgn(newClip, &theRect);
  626.             SectRgn(oldClip, newClip, newClip);
  627.             SetClip(newClip);
  628.             }
  629.         textHandle = (** hTE).hText;
  630.         textLength = (** hTE).teLength;
  631.         
  632.         offset = 0;
  633.         while (offset < textLength)
  634.             {
  635.             if (FindNextPicture(textHandle, &offset, &delta))
  636.                 {
  637.                 Handle        pictHandle;
  638.                 Point        picturePoint = TEGetPoint(offset, hTE);
  639.                 
  640.                 SetResLoad(false);
  641.                 pictHandle = Get1Resource('PICT', kPictureBase + pictIndex);
  642.                 SetResLoad(true);
  643.                 if (pictHandle)
  644.                     {
  645.                     PicHandle    pictHeader = (PicHandle)NewHandle(sizeof(Picture) + sizeof(long)*8);
  646.                     
  647.                     if (pictHeader)
  648.                         {
  649.                         HLock((Handle) pictHeader);
  650.                         ReadPartialResource(pictHandle, 0, (Ptr)*pictHeader, GetHandleSize((Handle)pictHeader));
  651.                         HUnlock((Handle) pictHeader);
  652.                         
  653.                         // calculate where to draw the picture, this location is
  654.                         // computed by:
  655.                         //  1) the frame of the original picture, normalized to 0,0
  656.                         //    2) the location of the non-breaking space character
  657.                         //    3) centering the picture on the content frame horizontally
  658.                         //    4) subtracting off the line-height of the character
  659.                         
  660.                         GetPICTRectangleAt72dpi(pictHeader, &theRect);
  661.                         OffsetRect(&theRect, -theRect.left, -theRect.top);
  662.                         OffsetRect(&theRect, 
  663.                                             theRect.left +
  664.                                             ((viewRect.right - viewRect.left) >> 1) -
  665.                                             ((theRect.right - theRect.left) >> 1),
  666.                                         picturePoint.v-theRect.top - pData->vScrollAmount);
  667.     
  668.                         // only draw the picture if it will be visible (vastly improves scrolling
  669.                         // performance in documents with many embedded pictures)
  670.                         
  671.                         if (RectInRgn(&theRect, qd.thePort->clipRgn))
  672.                             SpoolDrawPicture(pictHandle, pictHeader, &theRect);
  673.                         }
  674.                     ReleaseResource((Handle) pictHandle);
  675.                     }
  676.                 ++pictIndex;
  677.  
  678.                 offset += delta;
  679.                 }
  680.             else
  681.                 break;
  682.             }
  683.                 
  684.         SetClip(oldClip);
  685.         DisposeRgn(oldClip);
  686.         }
  687.         
  688.     UseResFile(oldResFile);
  689.     
  690. } // DrawPictures
  691.  
  692. // --------------------------------------------------------------------------------------------------------------
  693. static void UpdateFileInfo(FSSpec *pSpec, Boolean documentIsText)
  694. {
  695.     FInfo    theInfo;
  696.  
  697.     FSpGetFInfo(pSpec, &theInfo);
  698.     theInfo.fdCreator = 'ttxt';
  699.  
  700.     // set the stationary bit, if we must
  701.     if (!documentIsText)
  702.         {
  703.         theInfo.fdFlags |= kIsStationary;
  704.         theInfo.fdType = 'sEXT';
  705.         }
  706.     else
  707.         {
  708.         theInfo.fdFlags &= ~kIsStationary;
  709.         theInfo.fdType = 'TEXT';
  710.         }
  711.     FSpSetFInfo(pSpec, &theInfo);
  712.  
  713. } // UpdateFileInfo
  714.  
  715. // --------------------------------------------------------------------------------------------------------------
  716. static OSErr    TextSave(WindowDataPtr pData)
  717. {
  718.     OSErr    anErr = noErr;
  719.     long    amountToWrite;
  720.     
  721.     // write out the text
  722.     SetFPos(pData->dataRefNum, fsFromStart, 0);
  723.     amountToWrite = (** ((TextDataPtr) pData)->hTE).teLength;
  724.     anErr = FSWrite(pData->dataRefNum, &amountToWrite, * (** ((TextDataPtr) pData)->hTE).hText);
  725.     nrequire(anErr, FailedWrite);
  726.     SetEOF(pData->dataRefNum, amountToWrite);
  727.     
  728.     if (pData->resRefNum == -1)
  729.         {
  730.         FSpCreateResFile(&pData->fileSpec, 'ttxt', pData->originalFileType, 0);
  731.         pData->resRefNum = FSpOpenResFile(&pData->fileSpec, fsRdWrPerm);            
  732.         }
  733.     else
  734.         {
  735.         // a save always makes it into file of type 'TEXT', for Save As… we 
  736.         // afterwards set it again if the user is saving as stationary.
  737.         UpdateFileInfo(&pData->fileSpec, true);
  738.         }
  739.  
  740.     if (pData->resRefNum != -1)
  741.         {
  742.         short    oldResFile = CurResFile();
  743.         Handle    resourceHandle;
  744.         
  745.         UseResFile(pData->resRefNum);
  746.         
  747.         // remove any old sounds
  748.         resourceHandle = Get1Resource('snd ', kSoundBase);
  749.         if (resourceHandle)
  750.             {
  751.             RemoveResource(resourceHandle);
  752.             DisposeHandle(resourceHandle);
  753.             }
  754.             
  755.         // save the new sound
  756.         resourceHandle = ((TextDataPtr) pData)->soundHandle;
  757.         if (resourceHandle)
  758.             {
  759.             anErr = HandToHand(&resourceHandle);
  760.             if (anErr == noErr)
  761.                 {
  762.                 AddResource(resourceHandle, 'snd ', kSoundBase, "\p");
  763.                 anErr = ResError();
  764.                 }
  765.             nrequire(anErr, AddSoundResourceFailed);
  766.             }
  767.             
  768.         // remove any old styles
  769.         resourceHandle = Get1Resource('styl', 128);
  770.         if (resourceHandle)
  771.             {
  772.             RemoveResource(resourceHandle);
  773.             DisposeHandle(resourceHandle);
  774.             }
  775.         
  776.         // save the new style -- get the scrap handle from the TE record
  777.         // To do this, we must select the text -- BUT doing so through the
  778.         // TextEdit API results in lots of flashing and annoying behavior.
  779.         // So we just change the offsets by hand
  780.         {
  781.         short                oldStart, oldEnd;    
  782.         
  783.         oldStart = (** ((TextDataPtr) pData)->hTE).selStart;
  784.         oldEnd   = (** ((TextDataPtr) pData)->hTE).selEnd;
  785.         
  786.         (** ((TextDataPtr) pData)->hTE).selStart = 0;
  787.         (** ((TextDataPtr) pData)->hTE).selEnd = 32767;
  788.     
  789.         resourceHandle = (Handle) TEGetStyleScrapHandle( ((TextDataPtr) pData)->hTE );
  790.     
  791.         (** ((TextDataPtr) pData)->hTE).selStart = oldStart;
  792.         (** ((TextDataPtr) pData)->hTE).selEnd = oldEnd;
  793.         }
  794.     
  795.         if (resourceHandle)
  796.             {
  797.             AddResource(resourceHandle, 'styl', 128, "\p");
  798.             anErr = ResError();
  799.             nrequire(anErr, AddStyleResourceFailed);
  800.             }
  801.             
  802.         AddSoundResourceFailed:
  803.         AddStyleResourceFailed:    
  804.         
  805.             UpdateResFile(pData->resRefNum);
  806.             UseResFile(oldResFile);
  807.         }
  808.  
  809.         
  810. // FALL THROUGH EXCEPTION HANDLING
  811. FailedWrite:
  812.  
  813.     // if everything went okay, then clear the changed bit
  814.     if (anErr == noErr)
  815.         {
  816.         pData->changed = false;
  817.         (void) FlushVol("\p", pData->fileSpec.vRefNum);
  818.         }
  819.         
  820.     return anErr;
  821.     
  822. } // TextSave
  823.  
  824. // --------------------------------------------------------------------------------------------------------------
  825.  
  826. static pascal void DrawTextUserItem(DialogPtr dPtr, short theItem)
  827. /*
  828.     Draw text icon in the location
  829. */
  830. {
  831.     short        kind;
  832.     Handle        itemHandle;
  833.     Rect        box;
  834.     
  835.     GetDialogItem(dPtr, theItem, &kind, &itemHandle, &box);
  836.     PlotIconID(&box, ttNone, ttNone, kTextIcon);
  837.             
  838. } // DrawTextUserItem
  839.  
  840. #if GENERATINGCFM
  841.     static RoutineDescriptor gDrawTextUserItemRD = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawTextUserItem);
  842.     static UserItemUPP gDrawTextUserItem = &gDrawTextUserItemRD;
  843. #else
  844.     static UserItemUPP gDrawTextUserItem = NewUserItemProc(DrawTextUserItem);
  845. #endif
  846.  
  847. // --------------------------------------------------------------------------------------------------------------
  848.  
  849. static pascal void DrawStationeryUserItem(DialogPtr dPtr, short theItem)
  850. /*
  851.     Draw stationery icon in the location
  852. */
  853. {
  854.     short        kind;
  855.     Handle        itemHandle;
  856.     Rect        box;
  857.     
  858.     GetDialogItem(dPtr, theItem, &kind, &itemHandle, &box);
  859.     PlotIconID(&box, ttNone, ttNone, kStationeryIcon);
  860.             
  861. } // DrawStationeryUserItem
  862.  
  863.  
  864. #if GENERATINGCFM
  865.     static RoutineDescriptor gDrawStationeryUserItemRD = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawStationeryUserItem);
  866.     static UserItemUPP gDrawStationeryUserItem = &gDrawStationeryUserItemRD;
  867. #else
  868.     static UserItemUPP gDrawStationeryUserItem = NewUserItemProc(DrawStationeryUserItem);
  869. #endif
  870.  
  871.  
  872. // --------------------------------------------------------------------------------------------------------------
  873. // pre and post update procs for inline input 
  874.  
  875. static pascal void TSMPreUpdateProc(TEHandle textH, long refCon)
  876. {
  877. #pragma unused (refCon)
  878.  
  879.     ScriptCode     keyboardScript;
  880.     short        mode;
  881.     TextStyle    theStyle;
  882.  
  883.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  884.     mode = doFont;
  885.     if     (!
  886.             (
  887.             (TEContinuousStyle(&mode, &theStyle, textH) )&& 
  888.             (FontToScript(theStyle.tsFont) == keyboardScript) 
  889.             )
  890.         )
  891.         {
  892.         theStyle.tsFont = GetScriptVariable(keyboardScript, smScriptAppFond);
  893.         TESetStyle(doFont, &theStyle, false, textH);
  894.         }
  895.         
  896. } // TSMPreUpdateProc
  897.  
  898. #if GENERATINGCFM
  899.     static RoutineDescriptor gTSMPreUpdateProcRD = BUILD_ROUTINE_DESCRIPTOR(uppTSMTEPreUpdateProcInfo, TSMPreUpdateProc);
  900.     static TSMTEPreUpdateUPP gTSMPreUpdateProc = &gTSMPreUpdateProcRD;
  901. #else
  902.     static TSMTEPreUpdateUPP gTSMPreUpdateProc = NewTSMTEPreUpdateProc(TSMPreUpdateProc);
  903. #endif
  904.  
  905. static pascal void TSMPostUpdateProc(
  906.                 TEHandle textH,
  907.                 long fixLen,
  908.                 long inputAreaStart,
  909.                 long inputAreaEnd,
  910.                 long pinStart,
  911.                 long pinEnd,
  912.                 long refCon)
  913. {
  914. #pragma unused (textH, fixLen, inputAreaStart, inputAreaEnd, pinStart, pinEnd)
  915.  
  916.     AdjustScrollBars((WindowRef)refCon, false, false, nil);
  917.     AdjustTE((WindowDataPtr)refCon, true);
  918.     
  919.     ((WindowDataPtr)refCon)->changed = true;
  920.     
  921. } // TSMPostUpdateProc
  922.  
  923. #if GENERATINGCFM
  924.     static RoutineDescriptor gTSMPostUpdateProcRD = BUILD_ROUTINE_DESCRIPTOR(uppTSMTEPostUpdateProcInfo, TSMPostUpdateProc);
  925.     static TSMTEPostUpdateUPP gTSMPostUpdateProc = &gTSMPostUpdateProcRD;
  926. #else
  927.     static TSMTEPostUpdateUPP gTSMPostUpdateProc = NewTSMTEPostUpdateProc(TSMPostUpdateProc);
  928. #endif
  929.  
  930. // --------------------------------------------------------------------------------------------------------------
  931.  
  932. static pascal short SaveDialogHook(short item, DialogPtr dPtr, Boolean *isText)
  933. {
  934.     short    theType;
  935.     Handle    theHandle;
  936.     Rect    theRect;
  937.     short    returnValue = item;
  938.     
  939.  
  940.     switch (item)
  941.         {
  942.         case sfHookFirstCall:
  943.             if (GetWRefCon(GetDialogWindow(dPtr)) == sfMainDialogRefCon)
  944.                 {
  945.                 GetDialogItem(dPtr, iTextUserItem, &theType, &theHandle, &theRect);
  946.                 theHandle = (Handle) gDrawTextUserItem;
  947.                 SetDialogItem(dPtr, iTextUserItem, theType, theHandle, &theRect);
  948.                 
  949.                 GetDialogItem(dPtr, iStationeryUserItem, &theType, &theHandle, &theRect);
  950.                 theHandle = (Handle) gDrawStationeryUserItem;
  951.                 SetDialogItem(dPtr, iStationeryUserItem, theType, theHandle, &theRect);
  952.     
  953.                 GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  954.                 SetControlValue((ControlHandle) theHandle, 1);
  955.                 
  956.                 GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  957.                 SetControlValue((ControlHandle) theHandle, 0);
  958.                 *isText = true;
  959.                 }
  960.             break;
  961.             
  962.         case iTextDocumentItem:
  963.         case iTextUserItem:
  964.             GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  965.             SetControlValue((ControlHandle) theHandle, 1);
  966.             
  967.             GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  968.             SetControlValue((ControlHandle) theHandle, 0);
  969.             
  970.             *isText = true;
  971.             returnValue = sfHookNullEvent;
  972.             break;
  973.             
  974.         case iStationeryDocumentItem:
  975.         case iStationeryUserItem:
  976.             GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  977.             SetControlValue((ControlHandle) theHandle, 0);
  978.             
  979.             GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  980.             SetControlValue((ControlHandle) theHandle, 1);
  981.             
  982.             *isText = false;
  983.             returnValue = sfHookNullEvent;
  984.             break;
  985.         
  986.         }
  987.         
  988.     return returnValue;
  989.     
  990. } // SaveDialogHook
  991.  
  992. #if GENERATINGCFM
  993.     static RoutineDescriptor gSaveDialogHookRD = BUILD_ROUTINE_DESCRIPTOR(uppDlgHookYDProcInfo, SaveDialogHook);
  994.     static DlgHookYDUPP gSaveDialogHook = &gSaveDialogHookRD;
  995. #else
  996.     static DlgHookYDUPP gSaveDialogHook = NewDlgHookYDProc(SaveDialogHook);
  997. #endif
  998.  
  999. // --------------------------------------------------------------------------------------------------------------
  1000.  
  1001. // Handle update/activate events behind Standard File
  1002. static pascal Boolean SaveDialogFilter(DialogPtr theDialog, EventRecord *theEvent,
  1003.                                       short *itemHit, void *myDataPtr)
  1004. {
  1005.     #pragma unused(myDataPtr)
  1006.  
  1007.     if (StdFilterProc(theDialog, theEvent, itemHit))
  1008.         return true;
  1009.  
  1010.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  1011.     // drastically changing how the system handles the menu bar during our alert)
  1012.     if (theEvent->what == updateEvt /* || theEvent->what == activateEvt */ )
  1013.         {
  1014.         HandleEvent(theEvent);
  1015.         }
  1016.  
  1017.     return false;
  1018.  
  1019. } // SaveDialogFilter
  1020.  
  1021.  
  1022. #if GENERATINGCFM
  1023.     static RoutineDescriptor gSaveDialogFilterRD = BUILD_ROUTINE_DESCRIPTOR(uppModalFilterYDProcInfo, SaveDialogFilter);
  1024.     static ModalFilterYDUPP gSaveDialogFilter = &gSaveDialogFilterRD;
  1025. #else
  1026.     static ModalFilterYDUPP gSaveDialogFilter = NewModalFilterYDProc(SaveDialogFilter);
  1027. #endif
  1028.  
  1029. // --------------------------------------------------------------------------------------------------------------
  1030.  
  1031. static OSErr    TextSaveAs(WindowRef pWindow, WindowDataPtr pData)
  1032. {
  1033.     OSErr                anErr = noErr;
  1034.     short                oldRes, oldData;
  1035.     StandardFileReply    sfReply;
  1036.     Boolean                documentIsText;
  1037.     
  1038.     // save the old references -- if there is an error, we restore them
  1039.     oldRes = pData->resRefNum;
  1040.     oldData = pData->dataRefNum;
  1041.     
  1042.     // ask where and how to save this document
  1043.     {
  1044.     Str255    defaultName;
  1045.     Point    where = {-1, -1};
  1046.     
  1047.     // setup for the call
  1048.     GetWTitle(pWindow, defaultName);
  1049.     SetCursor(&qd.arrow);
  1050.     
  1051.     // find out where the user wants the file
  1052.     CustomPutFile("\p", defaultName, &sfReply, 
  1053.                 kTextSaveAsDialogID, where,
  1054.                 gSaveDialogHook, gSaveDialogFilter, nil, nil, &documentIsText);
  1055.     
  1056.     // map the cancel button into a cancelling error
  1057.     if (!sfReply.sfGood)
  1058.         anErr = eUserCanceled;
  1059.     }
  1060.         
  1061.     // can't replace over other types    
  1062.     if (sfReply.sfReplacing)
  1063.         {
  1064.         FInfo    theInfo;
  1065.         
  1066.         FSpGetFInfo(&sfReply.sfFile, &theInfo);
  1067.         
  1068.         if ( (theInfo.fdType != 'TEXT') && (theInfo.fdType != 'sEXT') )
  1069.             anErr = eDocumentWrongKind;
  1070.         }
  1071.     nrequire(anErr, StandardPutFile);
  1072.         
  1073.     if     (
  1074.         (sfReply.sfReplacing) && 
  1075.         (oldData != -1) && 
  1076.         (pData->fileSpec.vRefNum == sfReply.sfFile.vRefNum) &&
  1077.         (pData->fileSpec.parID == sfReply.sfFile.parID) &&
  1078.         EqualString(pData->fileSpec.name, sfReply.sfFile.name, false, false)
  1079.         )
  1080.         {
  1081.  
  1082.         anErr = TextSave(pData);
  1083.         
  1084.         if (anErr == noErr)
  1085.             UpdateFileInfo(&sfReply.sfFile, documentIsText);
  1086.         }
  1087.     else
  1088.         {
  1089.         // create the data file and resource fork
  1090.         (void) FSpDelete(&sfReply.sfFile);
  1091.         anErr = FSpCreate(&sfReply.sfFile, 'ttxt', documentIsText ? 'TEXT' : 'sEXT', 0);
  1092.         FSpCreateResFile(&sfReply.sfFile, 'ttxt', documentIsText ? 'TEXT' : 'sEXT', 0);
  1093.         nrequire(anErr, FailedCreate);
  1094.  
  1095.         // set the stationary bit, if we must
  1096.         if (!documentIsText)
  1097.             {
  1098.             FInfo    theInfo;
  1099.             
  1100.             FSpGetFInfo(&sfReply.sfFile, &theInfo);
  1101.             theInfo.fdFlags |= kIsStationary;
  1102.             FSpSetFInfo(&sfReply.sfFile, &theInfo);
  1103.             }
  1104.  
  1105.         // open both of forks
  1106.         anErr = FSpOpenDF(&sfReply.sfFile, fsRdWrPerm, &pData->dataRefNum);
  1107.         if (anErr == noErr)
  1108.             {
  1109.             pData->resRefNum = FSpOpenResFile(&sfReply.sfFile, fsRdWrPerm);
  1110.             anErr = ResError();
  1111.             }
  1112.         nrequire(anErr, FailedOpen);
  1113.  
  1114.         // call the standard save function to do the save
  1115.         anErr = TextSave(pData);
  1116.     
  1117.     // FALL THROUGH EXCEPTION HANDLING
  1118.     FailedOpen:
  1119.         FSpDelete(&sfReply.sfFile);    
  1120.     FailedCreate:
  1121.     StandardPutFile:
  1122.  
  1123.         // finally, close the old files if everything went okay
  1124.         if (anErr == noErr)
  1125.             {
  1126.             if (oldRes != -1)
  1127.                 CloseResFile(oldRes);
  1128.             if (oldData != -1)
  1129.                 FSClose(oldData);
  1130.             pData->isWritable = true;
  1131.             SetWTitle(pWindow, sfReply.sfFile.name);
  1132.             }
  1133.         else
  1134.             {
  1135.             pData->resRefNum = oldRes;
  1136.             pData->dataRefNum = oldData;
  1137.             }
  1138.         }
  1139.     
  1140.     // save new location
  1141.     if (anErr == noErr)
  1142.         BlockMoveData(&sfReply.sfFile, &pData->fileSpec, sizeof(FSSpec));
  1143.         
  1144. // Return eUserCanceled so we can avoid closing/quitting if they cancel the SF dialog
  1145. //    // don't propagate this error
  1146. //    if (anErr == eUserCanceled)
  1147. //        anErr = noErr;
  1148.  
  1149.     return anErr;
  1150.     
  1151. } // TextSaveAs
  1152.  
  1153. // --------------------------------------------------------------------------------------------------------------
  1154. static void ApplyFace(short requestedFace, WindowRef pWindow, WindowDataPtr pData, short commandID)
  1155. {
  1156.     TextStyle    style;
  1157.     short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1158.     
  1159.     SaveCurrentUndoState(pData, commandID);
  1160.     
  1161.     style.tsFace = requestedFace;
  1162.     TESetStyle(doFace + ((requestedFace != normal) ? doToggle : 0), &style, true, ((TextDataPtr) pData)->hTE);
  1163.     TECalText(((TextDataPtr) pData)->hTE);
  1164.     
  1165.     oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1166.     AdjustTE(pData, false);
  1167.     AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1168.  
  1169.     pData->changed = true;
  1170.     
  1171. } // ApplyFace
  1172.  
  1173. // --------------------------------------------------------------------------------------------------------------
  1174. static OSErr ApplySize(short requestedSize, WindowRef pWindow, WindowDataPtr pData, short commandID)
  1175. {
  1176.     OSErr        anErr = noErr;
  1177.     TextStyle    style;
  1178.     short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1179.     
  1180.     SaveCurrentUndoState(pData, commandID);
  1181.  
  1182.     style.tsSize = requestedSize;
  1183.     TESetStyle(doSize, &style, true, ((TextDataPtr) pData)->hTE);
  1184.     TECalText(((TextDataPtr) pData)->hTE);
  1185.     if (CalculateTextEditHeight(((TextDataPtr) pData)->hTE) > 32767)
  1186.         {        
  1187.         style.tsSize = 0;
  1188.         TESetStyle(doSize, &style, true, ((TextDataPtr) pData)->hTE);
  1189.         anErr = eDocumentTooLarge;
  1190.         }
  1191.  
  1192.     oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1193.     AdjustTE(pData, false);
  1194.     AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1195.  
  1196.     pData->changed = true;
  1197.     
  1198.     return anErr;
  1199.  
  1200. } // ApplySize
  1201.  
  1202. // --------------------------------------------------------------------------------------------------------------
  1203. // OOP INTERFACE ROUTINES
  1204. // --------------------------------------------------------------------------------------------------------------
  1205.  
  1206. static OSErr    TextUpdateWindow(WindowRef pWindow, WindowDataPtr pData)
  1207. {
  1208.     Rect    updateArea = pData->contentRect;
  1209.     
  1210.     // be sure to also erase the area where the horizontal scroll bar
  1211.     // is missing.
  1212.     updateArea.bottom = GetWindowPort(pWindow)->portRect.bottom;
  1213.     EraseRect(&updateArea);
  1214.     
  1215.     TEUpdate(&pData->contentRect, ((TextDataPtr) pData)->hTE);
  1216.     
  1217.     DrawPictures(pData, ((TextDataPtr) pData)->hTE);
  1218.     
  1219.     DrawControls(pWindow);
  1220.     DrawGrowIcon(pWindow);
  1221.     
  1222.     return noErr;
  1223.     
  1224. } // TextUpdateWindow
  1225.  
  1226. // --------------------------------------------------------------------------------------------------------------
  1227.  
  1228. static OSErr    TextCloseWindow(WindowRef pWindow, WindowDataPtr pData)
  1229. {
  1230. #pragma unused (pWindow)
  1231.  
  1232.     // shut down text services
  1233.     if (pData->docTSMDoc)
  1234.         {
  1235.         FixTSMDocument(pData->docTSMDoc);
  1236.         DeactivateTSMDocument(pData->docTSMDoc);
  1237.         DeleteTSMDocument(pData->docTSMDoc);
  1238.         }
  1239.     
  1240.     DisposeOfSpeech(true);
  1241.     DisposeHandle(((TextDataPtr) pData)->soundHandle);
  1242.     TEDispose(((TextDataPtr) pData)->hTE);
  1243.  
  1244.     DisposeHandle(((TextDataPtr) pData)->prevText);
  1245.     DisposeHandle(((TextDataPtr) pData)->prevStyle);
  1246.     
  1247.     TextRemoveContentsMenu(pData);
  1248.  
  1249.     return noErr;
  1250.  
  1251. } // TextCloseWindow
  1252.  
  1253. // --------------------------------------------------------------------------------------------------------------
  1254.  
  1255. static OSErr    TextActivateEvent(WindowRef pWindow, WindowDataPtr pData, Boolean activating)
  1256. {
  1257. #pragma unused (pWindow)
  1258.  
  1259.     // only modifyable docs can be active
  1260.     if (pData->originalFileType == 'TEXT')
  1261.         {
  1262.         if (activating)
  1263.             {
  1264.             TEActivate(((TextDataPtr) pData)->hTE);
  1265.             if (pData->docTSMDoc != nil)
  1266.                 ActivateTSMDocument(pData->docTSMDoc);
  1267.             }
  1268.         else
  1269.             {
  1270.             TEDeactivate(((TextDataPtr) pData)->hTE);
  1271.             if (pData->docTSMDoc != nil)
  1272.                 DeactivateTSMDocument(pData->docTSMDoc);
  1273.             }
  1274.         }
  1275.         
  1276.     // add contents menu, if appropriate        
  1277.     if (activating)
  1278.         {
  1279.         TextAddContentsMenu(pData);
  1280.         }
  1281.     else
  1282.         {
  1283.         TextRemoveContentsMenu(pData);
  1284.         }
  1285.     
  1286.     return noErr;
  1287.     
  1288. } // TextActivateEvent
  1289.  
  1290. // --------------------------------------------------------------------------------------------------------------
  1291.  
  1292. static Boolean    TextFilterEvent(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent)
  1293. {    
  1294.     switch (pEvent->what)
  1295.         {
  1296.         case nullEvent:
  1297.             if (pData->originalFileType == 'TEXT')
  1298.                 {
  1299.                 if ( pWindow == FrontWindow() )
  1300.                     TEIdle(((TextDataPtr) pData)->hTE);
  1301.                 }
  1302.                 
  1303.             // if we stop speaking, ditch the channel
  1304.             if (gSpeechChannel) 
  1305.                 {
  1306.                 SpeechStatusInfo    status;                // Status of our speech channel.
  1307.                 
  1308.                 if ( 
  1309.                     (GetSpeechInfo( gSpeechChannel, soStatus, (void*) &status ) == noErr)
  1310.                     &&  (!status.outputBusy )
  1311.                     )
  1312.                     DisposeOfSpeech(true);
  1313.                 }
  1314.             break;
  1315.         }
  1316.         
  1317.     return false;
  1318.     
  1319. } // TextFilterEvent
  1320.  
  1321. // --------------------------------------------------------------------------------------------------------------
  1322.  
  1323. static OSErr    TextScrollContent(WindowRef pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  1324. {
  1325.     GrafPtr        port = (GrafPtr) GetWindowPort(pWindow);
  1326.     RgnHandle    srcRgn, dstRgn;
  1327.     Rect        viewRect;
  1328.     
  1329.     // scroll the text area
  1330.     TEScroll(deltaH, deltaV, ((TextDataPtr) pData)->hTE);
  1331.  
  1332.     // calculate the region that is uncovered by the scroll
  1333.     srcRgn = NewRgn();
  1334.     dstRgn = NewRgn();
  1335.     viewRect = (**((TextDataPtr) pData)->hTE).viewRect;
  1336.     RectRgn( srcRgn, &viewRect );
  1337.     SectRgn( srcRgn, port->visRgn,  srcRgn );
  1338.     SectRgn( srcRgn, port->clipRgn, srcRgn );
  1339.     CopyRgn( srcRgn, dstRgn );
  1340.     OffsetRgn( dstRgn, deltaH, deltaV );
  1341.     SectRgn( srcRgn, dstRgn, dstRgn );
  1342.     DiffRgn( srcRgn, dstRgn, srcRgn );
  1343.  
  1344.     // clip to this new area
  1345.     GetClip(dstRgn);
  1346.     SetClip(srcRgn);
  1347.         DrawPictures(pData, ((TextDataPtr) pData)->hTE);
  1348.     SetClip(dstRgn);
  1349.     
  1350.     // all done with these calculation regions
  1351.     DisposeRgn( srcRgn );
  1352.     DisposeRgn( dstRgn );
  1353.     
  1354.     return eActionAlreadyHandled;
  1355.     
  1356. } // TextScrollContent
  1357.  
  1358. // --------------------------------------------------------------------------------------------------------------
  1359.  
  1360. static OSErr    TextKeyEvent(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent, Boolean isMotionKey)
  1361. {    
  1362.     OSErr    anErr = noErr;
  1363.     
  1364.     if (!(pEvent->modifiers & cmdKey)) 
  1365.         {
  1366.         char    theKey = pEvent->message & charCodeMask;
  1367.         char    theKeyCode = (pEvent->message >> 8) & charCodeMask;
  1368.         
  1369.         if ( ((theKey != kDeleteKey) || (theKeyCode != kForwardDeleteKey)) && 
  1370.             ((** ((TextDataPtr) pData)->hTE).teLength+1 > kMaxLength) )
  1371.             anErr = eDocumentTooLarge;
  1372.         else
  1373.             {
  1374.             long        oldHeight = CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  1375.             long        end = (**(((TextDataPtr) pData)->hTE)).selEnd;
  1376.             long        start = (**(((TextDataPtr) pData)->hTE)).selStart;
  1377.  
  1378.             ObscureCursor();
  1379.             SaveCurrentUndoState(pData, cTypingCommand);
  1380.             if (theKeyCode != kForwardDeleteKey)
  1381.                 {
  1382.                 if (pEvent->modifiers & shiftKey)
  1383.                     {
  1384.                     switch (theKeyCode)
  1385.                         {
  1386.                         case kUpArrow:
  1387.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1388.                             TESetSelect((**(((TextDataPtr) pData)->hTE)).selStart, end, ((TextDataPtr) pData)->hTE);
  1389.                             break;
  1390.                             
  1391.                         case kDownArrow:
  1392.                             TESetSelect(end, end, ((TextDataPtr) pData)->hTE);
  1393.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1394.                             TESetSelect(start, (**(((TextDataPtr) pData)->hTE)).selEnd, ((TextDataPtr) pData)->hTE);
  1395.                             break;
  1396.                             
  1397.                         case kRightArrow:
  1398.                             {
  1399.                             Handle textHandle = (**(((TextDataPtr) pData)->hTE)).hText;
  1400.                             Ptr textBuf;
  1401.                             char state;
  1402.                             
  1403.                             state = HGetState(textHandle);
  1404.                             HLock(textHandle);
  1405.                             textBuf = *(**(((TextDataPtr) pData)->hTE)).hText;
  1406.                             if (CharacterByteType(textBuf, start, smCurrentScript) != smSingleByte)
  1407.                                 ++end;
  1408.                             HSetState(textHandle, state);
  1409.                             TESetSelect(start, ++end, ((TextDataPtr) pData)->hTE);
  1410.                             }
  1411.                             break;
  1412.                             
  1413.                         case kLeftArrow:
  1414.                             if (start > 0)
  1415.                                 {
  1416.                                 if (start > 1)
  1417.                                     {
  1418.                                     Handle textHandle = (**(((TextDataPtr) pData)->hTE)).hText;
  1419.                                     Ptr textBuf;
  1420.                                     char state;
  1421.                                     
  1422.                                     state = HGetState(textHandle);
  1423.                                     HLock(textHandle);
  1424.                                     textBuf = *(**(((TextDataPtr) pData)->hTE)).hText;
  1425.                                     if (CharacterByteType(textBuf, start-1, smCurrentScript) != smSingleByte)
  1426.                                         --start;
  1427.                                     HSetState(textHandle, state);
  1428.                                     }
  1429.                                 TESetSelect(--start, end, ((TextDataPtr) pData)->hTE);
  1430.                                 }
  1431.                             break;
  1432.                             
  1433.                         default:
  1434.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1435.                         }
  1436.                     }
  1437.                 else
  1438.                     TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1439.                 }
  1440.             else
  1441.                 {
  1442.                 if     (end < (**(((TextDataPtr) pData)->hTE)).teLength)
  1443.                     {
  1444.                     if (start == end)
  1445.                         TEKey(kRightArrowCharCode, ((TextDataPtr) pData)->hTE);
  1446.                     TEKey(kBackspaceCharCode, ((TextDataPtr) pData)->hTE);
  1447.                     }
  1448.                 }
  1449.             oldHeight -= CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  1450.                             
  1451.             ((TextDataPtr) pWindow)->insideClickLoop = true;
  1452.                 AdjustTE(pData, false);
  1453.                 AdjustScrollBars(pWindow, (oldHeight > 0), (oldHeight > 0), nil);
  1454.             ((TextDataPtr) pWindow)->insideClickLoop = false;
  1455.  
  1456.             if (!isMotionKey)
  1457.                 pData->changed = true;
  1458.             }
  1459.         }
  1460.  
  1461.     return anErr;
  1462.  
  1463. } // TextKeyEvent
  1464.  
  1465. // --------------------------------------------------------------------------------------------------------------
  1466.  
  1467. static OSErr    TextContentClick(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent)
  1468. {
  1469.     OSErr            anErr = noErr;
  1470.     Point            clickPoint = pEvent->where;
  1471.     ControlHandle    theControl;
  1472.     RgnHandle        hilightRgn;
  1473.  
  1474.     GlobalToLocal(&clickPoint);
  1475.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  1476.         {
  1477.         if (gMachineInfo.haveDragMgr)
  1478.             {
  1479.             hilightRgn = NewRgn();
  1480.             TEGetHiliteRgn(hilightRgn, ((TextDataPtr) pData)->hTE);
  1481.     
  1482.             if (PtInRgn(clickPoint, hilightRgn))
  1483.                 {
  1484.                 SaveCurrentUndoState(pData, cTypingCommand);
  1485.                 if (!DragText(pWindow, pData, pEvent, hilightRgn))
  1486.                     anErr = eActionAlreadyHandled;
  1487.                 }
  1488.             else        
  1489.                 {
  1490.                 anErr = eActionAlreadyHandled;
  1491.                 }
  1492.     
  1493.             DisposeRgn(hilightRgn);
  1494.             }
  1495.         else
  1496.             {
  1497.             anErr = eActionAlreadyHandled;
  1498.             }
  1499.         }
  1500.  
  1501.     if ( (anErr == eActionAlreadyHandled) && (PtInRect(clickPoint, &pData->contentRect)) )
  1502.         {
  1503.         TEClick(clickPoint, (pEvent->modifiers & shiftKey) != 0, ((TextDataPtr) pData)->hTE);
  1504.         }
  1505.         
  1506.     return anErr;
  1507.     
  1508. } // TextContentClick
  1509.  
  1510. // --------------------------------------------------------------------------------------------------------------
  1511.  
  1512. static OSErr    TextAdjustSize(WindowRef pWindow, WindowDataPtr pData, 
  1513.             Boolean *didReSize) // input: was window resized, output: t->we resized something
  1514. {
  1515. #pragma unused (pWindow)
  1516.     
  1517.     if (*didReSize)
  1518.         {
  1519.         RecalcTE(pData, true);
  1520.         AdjustTE(pData, true);
  1521.         }
  1522.     else
  1523.         {
  1524.         AdjustTE(pData, false);
  1525.         }
  1526.         
  1527.     return noErr;
  1528.     
  1529. } // TextAdjustSize
  1530.  
  1531. // --------------------------------------------------------------------------------------------------------------
  1532.  
  1533. static short FontToQD(gxFont fontID, Style* styleBits)
  1534. {
  1535.     short            numFonts = GetHandleSize((Handle) gFontMappingList) / sizeof(FontMappingRecord);
  1536.     short            i;
  1537.     FontMappingPtr    pList = *gFontMappingList;
  1538.     
  1539.     for (i = 0; i < numFonts; ++i)
  1540.         {
  1541.         if (pList->fontID == fontID)
  1542.             {
  1543.             if (styleBits)
  1544.                 *styleBits = pList->qdStyle;
  1545.             return(pList->qdFont);
  1546.             }
  1547.         pList++;
  1548.         }
  1549.     
  1550.     // error case?   default.
  1551.     if (styleBits)
  1552.         *styleBits = normal;
  1553.     return(0);
  1554.     
  1555. } // FontToQD
  1556.  
  1557. // --------------------------------------------------------------------------------------------------------------
  1558.  
  1559. static gxFont QDToFont(short qdFont, Style styleBits)
  1560. {
  1561.     short            numFonts = GetHandleSize((Handle) gFontMappingList) / sizeof(FontMappingRecord);
  1562.     short            i;
  1563.     FontMappingPtr    pList;
  1564.     
  1565.     if (qdFont == systemFont)
  1566.         qdFont = GetSysFont();
  1567.     if (qdFont == applFont)
  1568.         qdFont = GetAppFont();
  1569.  
  1570.     // try to match both font and style
  1571.     pList = *gFontMappingList;
  1572.     for (i = 0; i < numFonts; ++i)
  1573.         {
  1574.         if ((pList->qdFont == qdFont) && (styleBits == pList->qdStyle))
  1575.             return(pList->fontID);
  1576.         pList++;
  1577.         }
  1578.     
  1579.     // if we can't match font and style, just look for font
  1580.     pList = *gFontMappingList;
  1581.     for (i = 0; i < numFonts; ++i)
  1582.         {
  1583.         if (pList->qdFont == qdFont)
  1584.             return(pList->fontID);
  1585.         pList++;
  1586.         }
  1587.     
  1588.     // error case?   default.
  1589.     return(nil);
  1590.     
  1591. } // QDToFont
  1592.  
  1593. // --------------------------------------------------------------------------------------------------------------
  1594.  
  1595. static OSErr    TextCommand(WindowRef pWindow, WindowDataPtr pData, short commandID, long menuResult)
  1596. {
  1597.  
  1598.     OSErr    anErr = noErr;
  1599.     
  1600.     SetPort((GrafPtr) GetWindowPort(pWindow));
  1601.     
  1602.     if ( (pData->docTSMDoc) && (menuResult != 0) )
  1603.         FixTSMDocument(pData->docTSMDoc);
  1604.         
  1605.     switch (commandID)
  1606.         {            
  1607.         case cUndo:
  1608.             {
  1609.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1610.  
  1611.             PerformUndo(pData);
  1612.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1613.             AdjustTE(pData, false);
  1614.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1615.             RecalcTE(pData, true);
  1616.             pData->changed = true;
  1617.             }
  1618.             break;
  1619.             
  1620.         case cCut:
  1621.             SaveCurrentUndoState(pData, cCut);
  1622.             {
  1623.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1624.  
  1625.             TECut(((TextDataPtr) pData)->hTE);    // no need for TEToScrap with styled TE record
  1626.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1627.             AdjustTE(pData, false);
  1628.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1629.             pData->changed = true;
  1630.             }
  1631.             break;
  1632.             
  1633.         case cCopy:
  1634.             TECopy(((TextDataPtr) pData)->hTE);    // no need for TEToScrap with styled TE record
  1635.             AdjustTE(pData, false);
  1636.             break;
  1637.             
  1638.         case cClear:
  1639.             SaveCurrentUndoState(pData, cClear);
  1640.             {
  1641.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1642.  
  1643.             TEDelete(((TextDataPtr) pData)->hTE);
  1644.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1645.             AdjustTE(pData, false);
  1646.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1647.             pData->changed = true;
  1648.             }
  1649.             break;
  1650.             
  1651.         case cPaste:
  1652.             SaveCurrentUndoState(pData, cPaste);
  1653.             {
  1654.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1655.  
  1656.             anErr = TEFromScrap();            
  1657.             if (anErr == noErr)
  1658.                 {                
  1659.                 // if the current length, plus the paste data, minus the data in the selection
  1660.                 // would make the document too large, say so
  1661.                 if     ( 
  1662.                         ((** ((TextDataPtr) pData)->hTE).teLength +
  1663.                         TEGetScrapLength() -
  1664.                         ((** ((TextDataPtr) pData)->hTE).selEnd-(** ((TextDataPtr) pData)->hTE).selStart)
  1665.                         )
  1666.                     > kMaxLength)
  1667.                     {
  1668.                     anErr = eDocumentTooLarge;
  1669.                     }
  1670.                 else
  1671.                     {
  1672.                     Handle    aHandle = (Handle) TEGetText(((TextDataPtr) pData)->hTE);
  1673.                     Size    oldSize = GetHandleSize(aHandle);
  1674.                     Size    newSize = oldSize + TEGetScrapLength();
  1675.                     OSErr    saveErr;
  1676.                     
  1677.                     SetHandleSize(aHandle, newSize);
  1678.                     saveErr = MemError();
  1679.                     SetHandleSize(aHandle, oldSize);
  1680.                     if (saveErr != noErr)
  1681.                         anErr = eDocumentTooLarge;
  1682.                     else
  1683.                         TEStylePaste(((TextDataPtr) pData)->hTE);
  1684.                     }
  1685.                     
  1686.                 UnloadScrap();
  1687.                 }
  1688.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1689.             AdjustTE(pData, false);
  1690.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1691.             pData->changed = true;
  1692.             }
  1693.             break;
  1694.             
  1695.         case cReplace:
  1696.             SaveCurrentUndoState(pData, cReplace);
  1697.             {
  1698.             short result = ConductFindOrReplaceDialog(kReplaceWindowID);
  1699.             
  1700.             if (result == cancel)
  1701.                 break;
  1702.                 
  1703.             if (result == iReplaceAll)
  1704.                 {
  1705.                 long         newStart, newEnd;
  1706.                 short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1707.                 
  1708.                 TESetSelect(0, 0, ((TextDataPtr) pData)->hTE);
  1709.                 while (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1710.                                     (**((TextDataPtr) pData)->hTE).selStart,
  1711.                                     gFindString, gCaseSensitive, false, false,
  1712.                                     &newStart, &newEnd))
  1713.                     {
  1714.                     TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1715.                     TEDelete(((TextDataPtr) pData)->hTE);
  1716.                     TEInsert(&gReplaceString[1], gReplaceString[0], ((TextDataPtr) pData)->hTE);
  1717.                     TESetSelect(newStart, newStart+gReplaceString[0], ((TextDataPtr) pData)->hTE);                
  1718.                     pData->changed = true;
  1719.                     }
  1720.                 oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1721.                 
  1722.                 AdjustTE(pData, false);
  1723.                 AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1724.     
  1725.                 anErr = eActionAlreadyHandled;
  1726.                 break;
  1727.                 }
  1728.             }
  1729.         
  1730.         // fall through from replace
  1731.         case cReplaceAgain:
  1732.             SaveCurrentUndoState(pData, cReplaceAgain);
  1733.             {
  1734.             long     newStart, newEnd;
  1735.             Boolean    isBackwards = ((gEvent.modifiers & shiftKey) != 0);
  1736.             
  1737.             if (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1738.                                 isBackwards ? (**((TextDataPtr) pData)->hTE).selEnd : (**((TextDataPtr) pData)->hTE).selStart,
  1739.                                 gFindString, gCaseSensitive, isBackwards, gWrapAround,
  1740.                                 &newStart, &newEnd))
  1741.                 {
  1742.                 short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1743.                 
  1744.                 TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1745.                 TEDelete(((TextDataPtr) pData)->hTE);
  1746.                 TEInsert(&gReplaceString[1], gReplaceString[0], ((TextDataPtr) pData)->hTE);
  1747.                 TESetSelect(newStart, newStart+gReplaceString[0], ((TextDataPtr) pData)->hTE);                
  1748.                 oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1749.                 
  1750.                 AdjustTE(pData, false);
  1751.                 AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1752.                 pData->changed = true;
  1753.                 }
  1754.             else
  1755.                 SysBeep(1);
  1756.  
  1757.             anErr = eActionAlreadyHandled;
  1758.             }
  1759.             break;
  1760.             
  1761.         case cFind:
  1762.         case cFindSelection:
  1763.             if (commandID == cFind)
  1764.                 {
  1765.                 if (ConductFindOrReplaceDialog(kFindWindowID) == cancel)    
  1766.                     break;
  1767.                 }
  1768.             else
  1769.                 {
  1770.                 gFindString[0] = (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart;
  1771.                 BlockMoveData( (*(**((TextDataPtr) pData)->hTE).hText) + (**((TextDataPtr) pData)->hTE).selStart, &gFindString[1], gFindString[0]);
  1772.                 }
  1773.             
  1774.         // fall through from find or find selection
  1775.         case cFindAgain:
  1776.             {
  1777.             long     newStart, newEnd;
  1778.             Boolean    isBackwards = ((gEvent.modifiers & shiftKey) != 0);
  1779.             
  1780.             if (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1781.                                 isBackwards ? (**((TextDataPtr) pData)->hTE).selStart : (**((TextDataPtr) pData)->hTE).selEnd,
  1782.                                 gFindString, gCaseSensitive, isBackwards, gWrapAround,
  1783.                                 &newStart, &newEnd))
  1784.                 {
  1785.                 TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1786.                 AdjustTE(pData, false);
  1787.                 AdjustScrollBars(pWindow, false, false, nil);
  1788.                 }
  1789.             else
  1790.                 SysBeep(1);
  1791.  
  1792.             anErr = eActionAlreadyHandled;
  1793.             }
  1794.             break;
  1795.             
  1796.         case cSelectAll:
  1797.             TESetSelect(0, (**((TextDataPtr) pData)->hTE).teLength, 
  1798.                         ((TextDataPtr) pData)->hTE);
  1799.             AdjustTE(pData, false);
  1800.             AdjustScrollBars(pWindow, false, false, nil);
  1801.             anErr = eActionAlreadyHandled;
  1802.             break;
  1803.         
  1804.         // save turns into save as if this is a new document or if the original wasn't
  1805.         // available for writing
  1806.         case cSave:
  1807.             if     (
  1808.                 (!pData->isWritable) || 
  1809.                 ( (pData->dataRefNum == -1) && (pData->resRefNum == -1) )
  1810.                 )
  1811.                 anErr = TextSaveAs(pWindow, pData);
  1812.             else
  1813.                 anErr = TextSave(pData);
  1814.             break;
  1815.             
  1816.         case cSaveAs:
  1817.             anErr = TextSaveAs(pWindow, pData);
  1818.             break;
  1819.             
  1820.         // SUPPORTED FONTS
  1821.         case cSelectFontStyle:            
  1822.         case cSelectFont:
  1823.             SaveCurrentUndoState(pData, cSelectFont);
  1824.             {
  1825.             Str255        itemName;
  1826.             Str255        menuName;
  1827.             TextStyle    theStyle;
  1828.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1829.             gxFont        fontID;
  1830.             
  1831.             GetMenuItemText( GetMenuHandle( menuResult>>16 ), menuResult & 0xFFFF, itemName );
  1832.             if (commandID == cSelectFont)
  1833.                 {
  1834.                 if (gMachineInfo.haveGX)    
  1835.                     {
  1836.                     GXFindFonts(nil, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, itemName[0], &itemName[1], 1,1, &fontID);
  1837.                     theStyle.tsFont = FontToQD(fontID, nil);
  1838.                     }
  1839.                 else
  1840.                     GetFNum( itemName, &theStyle.tsFont );
  1841.                     
  1842.                 TESetStyle( doFont, &theStyle, true, ((TextDataPtr) pData)->hTE );
  1843.                 }
  1844.             else
  1845.                 {
  1846.                 BlockMoveData((**GetMenuHandle( menuResult>>16 )).menuData, menuName, sizeof(Str255));
  1847.                 
  1848.                 // find the font based upon family and style names
  1849.                 GXFindFonts(nil, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, menuName[0], &menuName[1], 1,1, &fontID);
  1850.                 GXFindFonts(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, itemName[0], &itemName[1], 1,1, &fontID);
  1851.                 
  1852.                 // convert the font into a QD font ID and style bits (as appropriate)
  1853.                 theStyle.tsFont = FontToQD(fontID, &theStyle.tsFace);
  1854.                 TESetStyle( doFont + doFace, &theStyle, true, ((TextDataPtr) pData)->hTE );
  1855.                 }
  1856.                 
  1857.             TECalText(((TextDataPtr) pData)->hTE);
  1858.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1859.             AdjustTE(pData, false);
  1860.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1861.             pData->changed = true;
  1862.             }
  1863.             break;
  1864.             
  1865.             
  1866.         // SUPPORTED STYLES
  1867.         case cPlain:
  1868.             ApplyFace(normal, pWindow, pData, cPlain);
  1869.             break;
  1870.             
  1871.         case cBold:
  1872.             ApplyFace(bold, pWindow, pData, cBold);
  1873.             break;
  1874.  
  1875.         case cItalic:
  1876.             ApplyFace(italic, pWindow, pData, cItalic);
  1877.             break;
  1878.             
  1879.         case cUnderline:
  1880.             ApplyFace(underline, pWindow, pData, cUnderline);
  1881.             break;
  1882.             
  1883.         case cOutline:
  1884.             ApplyFace(outline, pWindow, pData, cOutline);
  1885.             break;
  1886.             
  1887.         case cShadow:
  1888.             ApplyFace(shadow, pWindow, pData, cShadow);
  1889.             break;
  1890.  
  1891.         case cCondensed:
  1892.             ApplyFace(condense, pWindow, pData, cCondensed);
  1893.             break;
  1894.             
  1895.         case cExtended:
  1896.             ApplyFace(extend, pWindow, pData, cExtended);
  1897.             break;
  1898.             
  1899.     
  1900.         // SUPPORTED SIZES
  1901.         case cSize9:
  1902.             anErr = ApplySize(9, pWindow, pData, cSize9);
  1903.             break;
  1904.             
  1905.         case cSize10:
  1906.             anErr = ApplySize(10, pWindow, pData, cSize10);
  1907.             break;
  1908.             
  1909.         case cSize12:
  1910.             anErr = ApplySize(12, pWindow, pData, cSize12);
  1911.             break;
  1912.             
  1913.         case cSize14:
  1914.             anErr = ApplySize(14, pWindow, pData, cSize14);
  1915.             break;
  1916.             
  1917.         case cSize18:
  1918.             anErr = ApplySize(18, pWindow, pData, cSize18);
  1919.             break;
  1920.             
  1921.         case cSize24:
  1922.             anErr = ApplySize(24, pWindow, pData, cSize24);
  1923.             break;
  1924.             
  1925.         case cSize36:
  1926.             anErr = ApplySize(36, pWindow, pData, cSize36);
  1927.             break;
  1928.             
  1929.             
  1930.         // SUPPORTED SOUND COMMANDS
  1931.         case cRecord:
  1932.             {
  1933.             Handle    tempHandle;
  1934.  
  1935.             // allocate our prefered buffer if we can, but if we can't
  1936.             // make sure that at least a minimum amount of RAM is around
  1937.             // before recording.
  1938.             tempHandle = NewHandle(kPrefBufferSize);
  1939.             anErr = MemError();
  1940.             if (anErr != noErr)
  1941.                 {
  1942.                 tempHandle = NewHandle(kMinBufferSize);
  1943.                 anErr = MemError();
  1944.                 DisposeHandle(tempHandle);
  1945.                 tempHandle = nil;
  1946.                 }
  1947.             
  1948.             // if the preflight goes okay, do the record
  1949.             if (anErr == noErr)
  1950.                 {
  1951.                 Point    where = {50, 100};
  1952.                 
  1953.                 anErr = SndRecord(nil, where, siGoodQuality, (SndListHandle*) &tempHandle);
  1954.                 if (anErr == noErr)
  1955.                     {
  1956.                     DisposeHandle(((TextDataPtr) pData)->soundHandle);
  1957.                     ((TextDataPtr) pData)->soundHandle = tempHandle;
  1958.                     pData->changed = true;
  1959.                     }
  1960.                 else
  1961.                     DisposeHandle(tempHandle);
  1962.                     
  1963.                 if (anErr == userCanceledErr)
  1964.                     anErr = noErr;
  1965.                 }
  1966.             }
  1967.             break;
  1968.             
  1969.         case cPlay:
  1970.             if (((TextDataPtr) pData)->soundHandle)
  1971.                 (void) SndPlay(nil, (SndListHandle) ((TextDataPtr) pData)->soundHandle, false);
  1972.             break;
  1973.  
  1974.         case cErase:
  1975.             DisposeHandle(((TextDataPtr) pData)->soundHandle);
  1976.             ((TextDataPtr) pData)->soundHandle = nil;
  1977.             pData->changed = true;
  1978.             break;
  1979.             
  1980.         case cSpeak:
  1981.             DisposeOfSpeech(false);
  1982.             if (gSpeechChannel == nil)
  1983.                 anErr = NewSpeechChannel( &gCurrentVoice, &gSpeechChannel );
  1984.                 
  1985.             if ( anErr == noErr )
  1986.                 {
  1987.                 short    textLength, textStart;
  1988.                                     
  1989.                 // determine which text to speak
  1990.                 if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  1991.                     {
  1992.                     textLength = (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart;
  1993.                     textStart = (**((TextDataPtr) pData)->hTE).selStart;
  1994.                     }
  1995.                 else                                            // No text selected.
  1996.                     {
  1997.                     textLength = (**((TextDataPtr) pData)->hTE).teLength;
  1998.                     textStart = 0;
  1999.                     }
  2000.                     
  2001.                     
  2002.                 gSpeakPtr = NewPtr(textLength);
  2003.                 anErr = MemError();
  2004.                 if (anErr == noErr)
  2005.                     {
  2006.                     BlockMoveData( *((**((TextDataPtr) pData)->hTE).hText) + textStart, gSpeakPtr, (Size) textLength );
  2007.                     anErr = SpeakText( gSpeechChannel, gSpeakPtr, textLength );
  2008.                     }
  2009.                 }
  2010.             break;
  2011.  
  2012.         case cStopSpeaking:
  2013.             DisposeOfSpeech(true);
  2014.             break;
  2015.             
  2016.         case cSelectVoiceSubMenu:
  2017.                 {
  2018.                 VoiceSpec    newSpec;
  2019.                 short        i, menuIndex;
  2020.                 Str255        itemText;
  2021.                 short        theVoiceCount;
  2022.                 MenuHandle     menu = GetMenuHandle(mVoices);
  2023.                 
  2024.                 // in order to change voices, we need to ditch the speaking
  2025.                 DisposeOfSpeech(true);
  2026.  
  2027.                 // get the name of the selected voice                
  2028.                 menuIndex = menuResult & 0xFFFF;
  2029.                 GetMenuItemText(menu, menuIndex, itemText);
  2030.                 
  2031.                 if (CountVoices( &theVoiceCount ) == noErr)
  2032.                     {
  2033.                     VoiceDescription    description;        // Info about a voice.
  2034.         
  2035.                     for (i = 1; i <= theVoiceCount; ++i)
  2036.                         {
  2037.                         if ( (GetIndVoice( i, &newSpec ) == noErr)  &&
  2038.                              (GetVoiceDescription( &newSpec, &description, sizeof(description) ) == noErr ) )
  2039.                             {
  2040.                             if (IUCompString( itemText, description.name ) == 0)
  2041.                                 break;
  2042.                             }
  2043.                         }
  2044.                     }
  2045.                     
  2046.                 gCurrentVoice = newSpec;
  2047.                 for (i = CountMItems(menu); i >= 1; --i)
  2048.                     CheckItem(menu, i, (menuIndex == i));
  2049.                 }
  2050.             break;
  2051.         
  2052.         
  2053.         case cSelectContents:
  2054.                 {
  2055.                 Str255    searchStr;
  2056.                 short    menuIndex;
  2057.                 long     newStart, newEnd;
  2058.                 
  2059.                 menuIndex = menuResult & 0xFFFF;
  2060.  
  2061.                 // get the search string for this menu item
  2062.                 anErr = TextGetContentsListItem(pData, menuIndex, nil, searchStr, nil);
  2063.                 
  2064.                 if (anErr == noErr)
  2065.                     {
  2066.                     if (PerformSearch(
  2067.                         (**((TextDataPtr) pData)->hTE).hText,
  2068.                         0,            // start at beginning of text
  2069.                         searchStr,
  2070.                         false,        // not case sensitive
  2071.                         false,        // forwards
  2072.                         false,        // wrap
  2073.                         &newStart, 
  2074.                         &newEnd))
  2075.                         {
  2076.                         
  2077.                         // <7>
  2078.                         
  2079.                         short     amount;
  2080.                         Point    newSelectionPt;
  2081.                         
  2082.                         // get QuickDraw offset of found text,  
  2083.                         // scroll that amount plus a line height,
  2084.                         // and add a fifth of the window for aesthetics (and  
  2085.                         // for slop to avoid fraction-of-line problems)
  2086.  
  2087.                         newSelectionPt = TEGetPoint(newEnd, ((TextDataPtr) pData)->hTE);
  2088.                         
  2089.                         amount = - newSelectionPt.v + pData->vScrollAmount;
  2090.                         amount += (pData->contentRect.bottom - pData->contentRect.top) / 5;
  2091.                         
  2092.                         SetControlAndClipAmount(pData->vScroll, &amount);
  2093.                         if (amount != 0)
  2094.                             {
  2095.                             DoScrollContent(pWindow, pData, 0, amount);
  2096.                             }
  2097.  
  2098.                         // move selection to beginning of found text 
  2099.                         // (are the Adjust calls necessary?)
  2100.                         
  2101.                         TESetSelect(newStart, newStart, ((TextDataPtr) pData)->hTE);    
  2102.                         AdjustTE(pData, false);
  2103.                         AdjustScrollBars(pWindow, false, false, nil);
  2104.  
  2105.                         }
  2106.                     else
  2107.                         {
  2108.                             // search failed
  2109.                             SysBeep(10);
  2110.                         }
  2111.                     }
  2112.                 }
  2113.             break;
  2114.         }
  2115.         
  2116.     return anErr;
  2117.     
  2118. } // TextCommand
  2119.  
  2120. // --------------------------------------------------------------------------------------------------------------
  2121.  
  2122. static OSErr    TextAdjustMenus(WindowRef pWindow, WindowDataPtr pData)
  2123. {
  2124. #pragma unused (pWindow)
  2125.  
  2126.     // enable the commands that we support for editable text document
  2127.     if (pData->originalFileType == 'TEXT')
  2128.         {        
  2129.         if (((TextDataPtr) pData)->prevCommandID != cNull)
  2130.             EnableCommand(cUndo);
  2131.             
  2132.         if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2133.             {
  2134.             EnableCommand(cCut);
  2135.             EnableCommand(cCopy);
  2136.             EnableCommand(cClear);
  2137.             
  2138.             EnableCommand(cFindSelection);
  2139.             }
  2140.         
  2141.         TEFromScrap();
  2142.         if (TEGetScrapLength() > 0)
  2143.             EnableCommand(cPaste);
  2144.             
  2145.         EnableCommand(cSaveAs);
  2146.         EnableCommand(cSelectAll);
  2147.         
  2148.         EnableCommand(cFind);
  2149.         EnableCommand(cReplace);
  2150.         if (gFindString[0] != 0)
  2151.             {
  2152.             EnableCommand(cFindAgain);
  2153.             EnableCommand(cReplaceAgain);
  2154.             }
  2155.             
  2156.         // enable all fonts, select the font current, if that's what's best
  2157.         EnableCommand(cSelectFont);
  2158.         {
  2159.         short        mode = doFont;
  2160.         Str255        fontName, itemName;
  2161.         Str255        styleName;
  2162.         TextStyle    theStyle;
  2163.         Boolean        isCont;
  2164.         gxFont        fontID;
  2165.         
  2166.         isCont = TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE);
  2167.         if (isCont)
  2168.             {
  2169.             if (gMachineInfo.haveGX)
  2170.                 {
  2171.                 fontID = QDToFont(theStyle.tsFont, theStyle.tsFace);
  2172.  
  2173.                 fontName[0] = GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &fontName[1], nil);
  2174.                 styleName[0] = GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &styleName[1], nil);
  2175.                 }
  2176.             else
  2177.                 {
  2178.                 GetFontName(theStyle.tsFont, fontName);
  2179.                 }
  2180.             }
  2181.  
  2182.  
  2183.             {
  2184.             MenuHandle    menu = GetMenuHandle( mFont );
  2185.             short        count = CountMItems(menu);
  2186.             short        index;
  2187.             
  2188.             for (index = 1; index <= count; ++index)
  2189.                 {
  2190.                 short    mark;
  2191.                 
  2192.                 GetItemMark(menu, index, &mark);
  2193.                 if (isCont)
  2194.                     {
  2195.                     GetMenuItemText( menu, index, itemName );
  2196.                     
  2197.                     // don't change the checkmark if it's a heirarchichal menu, because
  2198.                     // the mark actually holds the ID of sub-menu
  2199.                     if ((mark == noMark) || (mark == checkMark))
  2200.                         {
  2201.                         CheckItem(menu, index, EqualString(itemName, fontName, true, true) );
  2202.                         }
  2203.                     else
  2204.                         {
  2205.                         // if it is a sub menu, we check there too
  2206.                         MenuHandle    subMenu = GetMenuHandle(mark);
  2207.                         short        subCount = CountMItems(subMenu);
  2208.                         short        subIndex;
  2209.                         
  2210.                         if (EqualString(itemName, fontName, true, true))
  2211.                             {
  2212.                             SetItemStyle(menu, index, underline);
  2213.                             for (subIndex = 1; subIndex <= subCount; ++subIndex)
  2214.                                 {
  2215.                                 GetMenuItemText(subMenu, subIndex, itemName);
  2216.                                 CheckItem(subMenu, subIndex, EqualString(itemName, styleName, true, true) );
  2217.                                 }
  2218.                             }
  2219.                         else
  2220.                             {
  2221.                             SetItemStyle(menu, index, normal);
  2222.                             for (subIndex = 1; subIndex <= subCount; ++subIndex)
  2223.                                 CheckItem(subMenu, subIndex, false );
  2224.                             }
  2225.                         }
  2226.                     }
  2227.                 else
  2228.                     {
  2229.                     if ((mark == noMark) || (mark == checkMark))
  2230.                         CheckItem(menu, index, false);
  2231.                     else
  2232.                         SetItemStyle(menu, index, normal);
  2233.                     }
  2234.                 }
  2235.             }
  2236.         }
  2237.  
  2238.         // enable the sizes, and outline what's currently valid
  2239.         {
  2240.         short        mode;
  2241.         TextStyle    theStyle;
  2242.         Boolean        isCont;
  2243.         short        whichToCheck;
  2244.         
  2245.         // find out the continuous run of sizes
  2246.         whichToCheck = 0;
  2247.         mode = doSize;
  2248.         if (TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE))
  2249.             {            
  2250.             whichToCheck = theStyle.tsSize;
  2251.             
  2252.             // default font size -> proper size
  2253.             if (whichToCheck == 0)
  2254.                 whichToCheck = GetDefFontSize();
  2255.             }
  2256.             
  2257.         // find out the font runs
  2258.         mode = doFont;
  2259.         isCont = TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE);
  2260.         
  2261.         EnableCommandCheckStyle(cSize9,  whichToCheck == 9, (isCont & RealFont(theStyle.tsFont, 9)) ? outline : normal);
  2262.         EnableCommandCheckStyle(cSize10, whichToCheck == 10, (isCont & RealFont(theStyle.tsFont, 10)) ? outline : normal);
  2263.         EnableCommandCheckStyle(cSize12, whichToCheck == 12, (isCont & RealFont(theStyle.tsFont, 12)) ? outline : normal);
  2264.         EnableCommandCheckStyle(cSize14, whichToCheck == 14, (isCont & RealFont(theStyle.tsFont, 14)) ? outline : normal);
  2265.         EnableCommandCheckStyle(cSize18, whichToCheck == 18, (isCont & RealFont(theStyle.tsFont, 18)) ? outline : normal);
  2266.         EnableCommandCheckStyle(cSize24, whichToCheck == 24, (isCont & RealFont(theStyle.tsFont, 24)) ? outline : normal);
  2267.         EnableCommandCheckStyle(cSize36, whichToCheck == 36, (isCont & RealFont(theStyle.tsFont, 36)) ? outline : normal);            
  2268.         }
  2269.         
  2270.         {
  2271.         short        mode = doFace;
  2272.         TextStyle    theStyle;
  2273.         Style        legalStyles;
  2274.         
  2275.         if (!TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE))
  2276.             {
  2277.             theStyle.tsFace = normal;
  2278.             EnableCommandCheck(cPlain, false);
  2279.             }
  2280.         else
  2281.             EnableCommandCheck(cPlain, theStyle.tsFace == normal);
  2282.             
  2283.         // <39> use the script manager to determine legal styles for this
  2284.         // run of text.  If the legal styles are zero (trap unimplemented),
  2285.         // then we assume all styles.
  2286.         legalStyles = GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptValidStyles);
  2287.         if (legalStyles == 0)
  2288.             legalStyles = 0xFFFF;
  2289.             
  2290.         if (legalStyles & bold)
  2291.             EnableCommandCheck(cBold,             theStyle.tsFace & bold);
  2292.         if (legalStyles & italic)
  2293.             EnableCommandCheck(cItalic,         theStyle.tsFace & italic);
  2294.         if (legalStyles & underline)
  2295.             EnableCommandCheck(cUnderline,         theStyle.tsFace & underline);
  2296.         if (legalStyles & outline)
  2297.             EnableCommandCheck(cOutline,         theStyle.tsFace & outline);
  2298.         if (legalStyles & shadow)
  2299.             EnableCommandCheck(cShadow,         theStyle.tsFace & shadow);
  2300.         if (legalStyles & condense)
  2301.             EnableCommandCheck(cCondensed,         theStyle.tsFace & condense);
  2302.         if (legalStyles & extend)
  2303.             EnableCommandCheck(cExtended,         theStyle.tsFace & extend);
  2304.         }
  2305.         
  2306.         }
  2307.  
  2308.     // enable commands related to speaking the content if we have support for that
  2309.     if (gMachineInfo.haveTTS)
  2310.         {
  2311.         // if we are speaking, we can stop
  2312.         if (gSpeechChannel) 
  2313.             EnableCommand(cStopSpeaking);
  2314.  
  2315.         // even while speaking, you can re-speak or select a new voice
  2316.         EnableCommand(cSpeak);
  2317.         EnableCommand(cSelectVoice);
  2318.         EnableCommand(cSelectVoiceSubMenu);
  2319.         
  2320.         if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2321.             ChangeCommandName(cSpeak, kTextStrings, iSpeakSelection);
  2322.         else
  2323.             ChangeCommandName(cSpeak, kTextStrings, iSpeakAll);
  2324.             
  2325.         }
  2326.         
  2327.     // enable the correct controls to go with sound input/output
  2328.     if (((TextDataPtr) pData)->soundHandle)
  2329.         EnableCommand(cPlay);
  2330.     if (pData->originalFileType == 'TEXT')
  2331.         {
  2332.         if (((TextDataPtr) pData)->soundHandle)
  2333.             EnableCommand(cErase);
  2334.         else
  2335.             {
  2336.             if (gMachineInfo.haveRecording)
  2337.                 EnableCommand(cRecord);
  2338.             }
  2339.         }
  2340.     
  2341.     // enable the contents menu, if any         
  2342.     (void) TextAdjustContentsMenu(pData);
  2343.     
  2344.     // enable commands that we support at all times
  2345.     if (GetControlMaximum(pData->vScroll) != 0)
  2346.         {
  2347.         EnableCommand(cNextPage);
  2348.         EnableCommand(cPreviousPage);
  2349.         }
  2350.     
  2351.     return noErr;
  2352.     
  2353. } // TextAdjustMenus
  2354.  
  2355. // --------------------------------------------------------------------------------------------------------------
  2356.  
  2357. static OSErr    TextGetDocumentRect(WindowRef pWindow, WindowDataPtr pData, 
  2358.             LongRect * documentRectangle, Boolean forGrow)
  2359. {
  2360. #pragma unused (pWindow)
  2361.     
  2362.     Rect    theRect = pData->contentRect;
  2363.     Rect    maxRect = (**GetGrayRgn()).rgnBBox;
  2364.     
  2365.     if ( (!forGrow) && (!(((TextDataPtr) pData)->insideClickLoop) ) )
  2366.         RecalcTE(pData, false);
  2367.     
  2368.     theRect.bottom = CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  2369.     theRect.bottom += kMargins*2;
  2370.     theRect.right = maxRect.right;
  2371.         
  2372.     if (theRect.bottom < pData->contentRect.bottom)
  2373.         theRect.bottom = pData->contentRect.bottom;
  2374.         
  2375.     if (forGrow)
  2376.         theRect.bottom = maxRect.bottom-kScrollBarSize;
  2377.         
  2378.     RectToLongRect(&theRect, documentRectangle);
  2379.     
  2380.     return noErr;
  2381.     
  2382. } // TextGetDocumentRect
  2383.  
  2384. // --------------------------------------------------------------------------------------------------------------
  2385.  
  2386. static OSErr    TextGetBalloon(WindowRef pWindow, WindowDataPtr pData, 
  2387.         Point *localMouse, short * returnedBalloonIndex, Rect *returnedRectangle)
  2388. {
  2389. #pragma unused (pWindow, pData, localMouse, returnedRectangle)
  2390.  
  2391.     *returnedBalloonIndex = iHelpTextContent;
  2392.     
  2393.     return noErr;
  2394.     
  2395. } // TextGetBalloon
  2396.  
  2397. // --------------------------------------------------------------------------------------------------------------
  2398.  
  2399. static long TextCalculateIdleTime(WindowRef pWindow, WindowDataPtr pData)
  2400. {
  2401. #pragma unused (pWindow, pData)
  2402.  
  2403.     if ( (gMachineInfo.amInBackground) || (! (**(((TextDataPtr) pData)->hTE)).active) )
  2404.         return(0x7FFFFFF);
  2405.     else
  2406.         return(1);
  2407.         
  2408. } // TextCalculateIdleTime
  2409.  
  2410. // --------------------------------------------------------------------------------------------------------------
  2411.  
  2412. static OSErr    TextAdjustCursor(WindowRef pWindow, WindowDataPtr pData, 
  2413.                         Point * localMouse, Rect * globalRect)
  2414. {
  2415. #pragma unused (pWindow, globalRect)
  2416.  
  2417.     OSErr            anErr = noErr;
  2418.     CursHandle        theCross;
  2419.     RgnHandle        hilightRgn;
  2420.     
  2421.     if (gMachineInfo.haveDragMgr)
  2422.         {
  2423.         hilightRgn = NewRgn();
  2424.         TEGetHiliteRgn(hilightRgn, ((TextDataPtr) pData)->hTE);
  2425.         if (PtInRgn(*localMouse, hilightRgn))
  2426.             {
  2427.             SetCursor(&qd.arrow);
  2428.             DisposeRgn(hilightRgn);
  2429.             return eActionAlreadyHandled;    
  2430.             }
  2431.     
  2432.         DisposeRgn(hilightRgn);
  2433.         }
  2434.         
  2435.     theCross = GetCursor(iBeamCursor);
  2436.     if (theCross)
  2437.         {
  2438.         char    oldState;
  2439.         
  2440.         oldState = HGetState((Handle) theCross);
  2441.         HLock((Handle) theCross);
  2442.         SetCursor(*theCross);
  2443.         HSetState((Handle) theCross, oldState);
  2444.         anErr = eActionAlreadyHandled;
  2445.         }
  2446.         
  2447.     return anErr;
  2448.     
  2449. } // TextAdjustCursor
  2450.  
  2451. // --------------------------------------------------------------------------------------------------------------
  2452.  
  2453. short gNilCaretProc[] = { 
  2454.                         0x584F,         // ADDQ.W    #$4, A7
  2455.                         0x4E75};        // RTS
  2456.  
  2457. // --------------------------------------------------------------------------------------------------------------
  2458.  
  2459. static OSErr    TextPrintPage(WindowRef pWindow, WindowDataPtr pData,
  2460.                     Rect * pageRect, long *pageNum)
  2461. {
  2462. #pragma unused (pWindow)
  2463.  
  2464.     OSErr        anErr = noErr;
  2465.     short        footerHeight;
  2466.     TEHandle    hTE;
  2467.     Rect        areaForThisPage;
  2468.     short        ourPage = 1;
  2469.     Boolean        documentHasFormControl = Count1Resources(kFormResource) != 0;
  2470.     
  2471.     // calculate area for the footer (page number)
  2472.     {
  2473.     FontInfo    theInfo;
  2474.     
  2475.     TextFont(0);
  2476.     TextSize(0);
  2477.     TextFace(normal);
  2478.     GetFontInfo(&theInfo);
  2479.     footerHeight = (theInfo.ascent + theInfo.descent + theInfo.leading) << 1;
  2480.     }
  2481.     
  2482.     // duplicate the text edit record, disable the selection before swapping the new port in
  2483.     hTE = ((TextDataPtr) pData)->hTE;
  2484.     TEDeactivate(hTE);
  2485.     
  2486.     anErr = HandToHand((Handle*) &hTE);
  2487.     nrequire(anErr, DuplicateTE);
  2488.  
  2489.     // turn off outline hilighting -- because the window is disabled while
  2490.     // printing is going on, but we don't want that disabled hilight to draw
  2491.     TEFeatureFlag(teFOutlineHilite, teBitClear, ((TextDataPtr) pData)->hTE);
  2492.         
  2493.     // now HERE'S a real hack!  Under certain conditions, Text Edit will draw the
  2494.     // cursor, even if you said the edit record is inactive!  This happens when
  2495.     // the internal state sez that the cursor hasn't been drawn yet.  Lucky
  2496.     // for us, the caret is drawn through a hook, which we replace with a NOP.
  2497.     (**hTE).caretHook = (CaretHookUPP) gNilCaretProc;
  2498.     
  2499.     // point the rectangles to be the page rect minus the footer
  2500.     areaForThisPage = *pageRect;
  2501.     areaForThisPage.bottom -= footerHeight;
  2502.     if (gMachineInfo.haveGX)
  2503.         InsetRect(&areaForThisPage, kGXPrintMargins, kGXPrintMargins);
  2504.     else
  2505.         InsetRect(&areaForThisPage, kPrintMargins, kPrintMargins);
  2506.     (**hTE).viewRect = (**hTE).destRect = areaForThisPage;
  2507.  
  2508.     // recalculate the line breaks
  2509.     TECalText(hTE);
  2510.  
  2511.     // point it at the printing port.
  2512.     (**hTE).inPort = qd.thePort;
  2513.  
  2514.     // now loop over all pages doing page breaking until we find our current
  2515.     // page, which we print, and then return.
  2516.     {
  2517.     Rect    oldPageHeight = (**hTE).viewRect;
  2518.     short    currentLine = 0;
  2519.     long    prevPageHeight = 0;
  2520.  
  2521.     while (ourPage <= *pageNum)
  2522.         {
  2523.         long    currentPageHeight = 0;
  2524.                     
  2525.         // calculate the height including the current page, breaks
  2526.         // when one of three things happen:
  2527.         // 1) adding another line to this page would go beyond the length of the page
  2528.         // 2) a picture needs to be broken to the next page (NOT YET IMPLEMENTED)
  2529.         // 3) we run out of lines for the document 
  2530.         // 4) if the line has a page break (defined as a non breaking space w/o a PICT)
  2531.  
  2532.         // POTENTIAL BUG CASES:
  2533.         // If a single line > the page height.  Can that happen?  If so, we need to 
  2534.         // add something to handle it.
  2535.         do
  2536.             {
  2537.             long currentLineHeight;
  2538.             
  2539.             // zero based count -- but one based calls to TEGetHeight 
  2540.             currentLineHeight = TEGetHeight(currentLine+1, currentLine+1, hTE);
  2541.                 
  2542.             // if adding this line would just be too much, break out of here
  2543.             if ((currentLineHeight + currentPageHeight) > (areaForThisPage.bottom - areaForThisPage.top))
  2544.                 break;
  2545.                 
  2546.             ++ currentLine;
  2547.             currentPageHeight += currentLineHeight;
  2548.             
  2549.             // if this line had a page break on it, break out of pagination
  2550.             if (documentHasFormControl && LineHasPageBreak(currentLine-1, hTE))
  2551.                 break;
  2552.                 
  2553.             } while (currentLine < (**hTE).nLines);
  2554.         
  2555.         // if this the page we are trying to print
  2556.         if (ourPage == *pageNum)
  2557.             {
  2558.             Str255        pageString;
  2559.             RgnHandle    oldRgn = NewRgn();
  2560.             
  2561.             // move onto the next page via offset by the previous pages -- but
  2562.             // clip to the current page height because we wouldn't want to see
  2563.             // half of a line from the next page at the bottom of a page.
  2564.             OffsetRect(&oldPageHeight, 0, -(prevPageHeight));
  2565.             oldPageHeight.bottom = oldPageHeight.top + currentPageHeight;
  2566.             (**hTE).destRect = oldPageHeight;
  2567.             
  2568.             // clip to this area as well
  2569.             areaForThisPage.bottom = areaForThisPage.top + currentPageHeight;
  2570.             GetClip(oldRgn);
  2571.             ClipRect(&areaForThisPage);
  2572.             
  2573.             // draw the edit record, plus our cool pictures    
  2574.             TEUpdate(&areaForThisPage, hTE); 
  2575.             DrawPictures(pData, hTE);
  2576.             
  2577.             // restore the clip
  2578.             SetClip(oldRgn);
  2579.             DisposeRgn(oldRgn);
  2580.             
  2581.             // Draw the page string at the bottom of the page, centered
  2582.             pageString[0] = 2;
  2583.             pageString[1] = '-';
  2584.             NumToString(*pageNum, &pageString[2]);
  2585.             pageString[0] += pageString[2];
  2586.             pageString[2] = ' ';
  2587.             pageString[++pageString[0]] = ' ';
  2588.             pageString[++pageString[0]] = '-';
  2589.             
  2590.             MoveTo(
  2591.                 pageRect->left + 
  2592.                     ((pageRect->right - pageRect->left) >> 1) - 
  2593.                     (StringWidth(pageString)>>1),
  2594.                 pageRect->bottom - kPrintMargins);
  2595.                 
  2596.             DrawString(pageString);
  2597.             
  2598.             // if we have completed all pages
  2599.             if (currentLine >= (**hTE).nLines)
  2600.                 {
  2601.                 // tell it to stop printing
  2602.                 *pageNum = -1;
  2603.                 }
  2604.                 
  2605.             // get out of here!
  2606.             break;
  2607.             }
  2608.     
  2609.         // move onto the next page via count
  2610.         ++ourPage;
  2611.         
  2612.         // and the list of pages before now includes this page we just finished
  2613.         prevPageHeight += currentPageHeight;
  2614.         
  2615.         }
  2616.     
  2617.     }
  2618.     
  2619.     
  2620.     // restore text for visible page if done
  2621.     if (*pageNum == -1)
  2622.         {
  2623.         TEFeatureFlag(teFOutlineHilite, teBitSet, ((TextDataPtr) pData)->hTE);
  2624.         TECalText(((TextDataPtr) pData)->hTE);
  2625.         if (pData->originalFileType != 'ttro')
  2626.             TEActivate(((TextDataPtr) pData)->hTE);
  2627.         }
  2628.         
  2629. // FALL THROUGH EXCEPTION HANDLING
  2630.  
  2631.     // Dispose this way to avoid disposing of any owned objects
  2632.     DisposeHandle((Handle) hTE);
  2633. DuplicateTE:
  2634.  
  2635.     return anErr;
  2636.     
  2637. } // TextPrintPage
  2638.  
  2639. // --------------------------------------------------------------------------------------------------------------
  2640.  
  2641. static OSErr    TextMakeWindow(WindowRef pWindow, WindowDataPtr pData)
  2642. {
  2643.     #pragma unused(pWindow)
  2644.  
  2645.     OSErr                anErr = noErr;
  2646.  
  2647.     pData->bumpUntitledCount    = true;
  2648.     
  2649.     pData->pScrollContent         = (ScrollContentProc)    TextScrollContent;
  2650.     pData->pAdjustSize             = (AdjustSizeProc)        TextAdjustSize;
  2651.     pData->pGetDocumentRect     = (GetDocumentRectProc)    TextGetDocumentRect;
  2652.     pData->pAdjustMenus             = (AdjustMenusProc)        TextAdjustMenus;
  2653.     pData->pCommand                 = (CommandProc)            TextCommand;
  2654.  
  2655.     pData->pCloseWindow         = (CloseWindowProc)        TextCloseWindow;
  2656.     pData->pFilterEvent         = (FilterEventProc)        TextFilterEvent;
  2657.     pData->pActivateEvent         = (ActivateEventProc)    TextActivateEvent;
  2658.     pData->pUpdateWindow         = (UpdateWindowProc)    TextUpdateWindow;
  2659.     pData->pPrintPage             = (PrintPageProc)        TextPrintPage;
  2660.  
  2661.     // we only support keydowns and editing for modifable docs
  2662.     if (pData->originalFileType != 'ttro')
  2663.         {
  2664.         pData->pKeyEvent             = (KeyEventProc)            TextKeyEvent;
  2665.         pData->pContentClick        = (ContentClickProc)        TextContentClick;
  2666.         pData->pAdjustCursor        = (AdjustCursorProc)        TextAdjustCursor;
  2667.         pData->pGetBalloon            = (GetBalloonProc)            TextGetBalloon;
  2668.         pData->pCalculateIdleTime    = (CalculateIdleTimeProc)    TextCalculateIdleTime;
  2669.         
  2670.         // We can always reference our Drag handlers, because they will not be called if we
  2671.         // don't have the Drag Manager available. We needn't check here (it would be redundant).
  2672.         pData->pDragTracking        = (DragTrackingProc)    TextDragTracking;
  2673.         pData->pDragReceive            = (DragReceiveProc)        TextDragReceive;
  2674.  
  2675.         pData->documentAcceptsText    = true;
  2676.         }
  2677.         
  2678.     // leave room for the grow area at bottom
  2679.     pData->hasGrow                = true;
  2680.     pData->contentRect.bottom    -= kScrollBarSize;    
  2681.     if ((pData->contentRect.right - pData->contentRect.left) > kOnePageWidth)
  2682.         pData->contentRect.right = pData->contentRect.left + kOnePageWidth;
  2683.         
  2684.     ((TextDataPtr) pData)->hTE    = TEStyleNew(&pData->contentRect, &pData->contentRect);
  2685.     anErr = MemError();
  2686.     nrequire(anErr, TENewFailed);
  2687.         
  2688.     pData->hScrollAmount        = 0;
  2689.     pData->vScrollAmount        = TEGetHeight(0, 0, ((TextDataPtr) pData)->hTE);
  2690.  
  2691.     TEAutoView(true, ((TextDataPtr) pData)->hTE);
  2692.  
  2693.     // Setup our click loop to handle autoscrolling
  2694.     ((TextDataPtr) pData)->docClick = (**(((TextDataPtr) pData)->hTE)).clickLoop;
  2695.     TESetClickLoop(gMyClickLoop, ((TextDataPtr) pData)->hTE);
  2696.     
  2697.     // if we have a data fork, read the contents into the record    
  2698.     if (pData->dataRefNum != -1)
  2699.         {
  2700.         long    dataSize;
  2701.         
  2702.         GetEOF(pData->dataRefNum, &dataSize);
  2703.         if (dataSize > kMaxLength)
  2704.             anErr = eDocumentTooLarge;
  2705.         else
  2706.             {
  2707.             Handle    tempHandle = NewHandle(dataSize);
  2708.             anErr = MemError();
  2709.             if (anErr == noErr)
  2710.                 {
  2711.                 // read the text in
  2712.                 SetFPos(pData->dataRefNum, fsFromStart, 0);
  2713.                 anErr = FSRead(pData->dataRefNum, &dataSize, * tempHandle);                
  2714.  
  2715.                 // then insert it.
  2716.                 if (anErr == noErr)
  2717.                     {
  2718.                     HLock(tempHandle);
  2719.                     TEStyleInsert(*tempHandle, dataSize, nil, ((TextDataPtr) pData)->hTE);
  2720.                     anErr = MemError();
  2721.                     }
  2722.                 DisposeHandle(tempHandle);
  2723.                 }
  2724.             
  2725.             }
  2726.         
  2727.         }
  2728.     nrequire(anErr, ReadData);
  2729.     
  2730.     // if we have a resource fork, read the contents
  2731.     if (pData->resRefNum != -1)
  2732.         {
  2733.         short    oldResFile = CurResFile();
  2734.         Handle    theStyle;
  2735.         
  2736.         // read the style information
  2737.         UseResFile(pData->resRefNum);
  2738.         theStyle = Get1Resource('styl', 128);
  2739.         if (theStyle)
  2740.             {
  2741.             HNoPurge(theStyle);
  2742.             TEUseStyleScrap(0, 32767, (StScrpHandle) theStyle, true, ((TextDataPtr) pData)->hTE);
  2743.             ReleaseResource(theStyle);
  2744.             }
  2745.  
  2746.         // if we have sound, load it in and detach it
  2747.         {
  2748.         Handle    soundHandle = Get1Resource('snd ', kSoundBase);
  2749.         if (soundHandle)
  2750.             {
  2751.             HNoPurge(soundHandle);
  2752.             DetachResource(soundHandle);
  2753.             ((TextDataPtr) pData)->soundHandle = soundHandle;
  2754.             }
  2755.         }
  2756.  
  2757.  
  2758.         UseResFile(oldResFile);
  2759.         }
  2760.             
  2761.     // hook out drawing of the non-breaking space for read only documents,
  2762.     // for modifiable documents, enable outline hiliting (ie, when TE window
  2763.     // isn't in front, show the gray outline)
  2764.     if (pData->originalFileType == 'ttro')
  2765.         {
  2766.         UniversalProcPtr    hookRoutine = (UniversalProcPtr)gMyDrawGlue;
  2767.         
  2768.         TECustomHook(intDrawHook, &hookRoutine, ((TextDataPtr) pData)->hTE);
  2769.         }
  2770.     else
  2771.         {
  2772.         TEFeatureFlag(teFOutlineHilite, teBitSet, ((TextDataPtr) pData)->hTE);
  2773.         }
  2774.  
  2775.     // make a TSM document if this is editable
  2776.     if     (
  2777.         (pData->originalFileType != 'ttro') &&
  2778.         (gMachineInfo.haveTSMTE)
  2779.         )
  2780.         {
  2781.         OSType    supportedInterfaces[1];
  2782.  
  2783.         supportedInterfaces[0] = kTSMTEInterfaceType;
  2784.         
  2785.         if (NewTSMDocument(1, supportedInterfaces, 
  2786.             &pData->docTSMDoc, (long)&pData->docTSMRecHandle) == noErr)
  2787.             {
  2788.             long response;
  2789.  
  2790.             (**(pData->docTSMRecHandle)).textH                 = ((TextDataPtr) pData)->hTE;
  2791.             if ((Gestalt(gestaltTSMTEVersion, &response) == noErr) && (response == gestaltTSMTE1))
  2792.                 (**(pData->docTSMRecHandle)).preUpdateProc     = gTSMPreUpdateProc;
  2793.             (**(pData->docTSMRecHandle)).postUpdateProc     = gTSMPostUpdateProc;
  2794.             (**(pData->docTSMRecHandle)).updateFlag         = kTSMTEAutoScroll;
  2795.             (**(pData->docTSMRecHandle)).refCon             = (long)pData;
  2796.             }
  2797.         }
  2798.  
  2799.     // now we have added text, so adjust views and such as needed
  2800.     TESetSelect(0, 0, ((TextDataPtr) pData)->hTE);
  2801.     RecalcTE(pData, true);
  2802.     AdjustTE(pData, true);
  2803.  
  2804.     // ???? Hack to get around a 7.0 TextEdit bug.  If you are pasting a multiple
  2805.     // line clipboard into TE, *and* the TextEdit record is new, *and* the selection
  2806.     // is at the begining of the doc (0,0 as above), *and* you haven't moved the
  2807.     // cursor around at all, then TE pastes thinking it's at the end of the line,
  2808.     // when it really should be at the begining.  Then if you <cr> with the cursor
  2809.     // visible, it'll leave a copy behind.  
  2810.     
  2811.     // I'm not happy with this, but I don't know another way around the problem.
  2812.     if (pData->originalFileType != 'ttro') 
  2813.         {
  2814.         TEKey(0x1F, ((TextDataPtr) pData)->hTE);
  2815.         TEKey(0x1E, ((TextDataPtr) pData)->hTE);
  2816.         }
  2817.     
  2818.     // <39> if this is a new document, convert the "system size", "system font", and
  2819.     // "application font" into real font IDs and sizes.  This is so that
  2820.     // if someone saves this document and opens it with another script
  2821.     // system, they don't get all huffy that the font changed on them.
  2822.     // It also solves problems with cut and paste to applications too stupid
  2823.     // to know that "zero" means system size.
  2824.     if (pData->dataRefNum == -1)
  2825.         {
  2826.         TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  2827.         short        mode = doAll;
  2828.         TextStyle    theStyle;
  2829.     
  2830.         TEContinuousStyle(&mode, &theStyle, hTE);
  2831.         if (theStyle.tsSize == 0)
  2832.             theStyle.tsSize = GetDefFontSize();
  2833.         if (theStyle.tsFont == systemFont)
  2834.             theStyle.tsFont = GetSysFont();
  2835.         if (theStyle.tsFont == applFont)
  2836.             theStyle.tsFont = GetAppFont();
  2837.             
  2838.         mode = doAll;
  2839.         TESetStyle(mode, &theStyle, false, hTE);
  2840.         }
  2841.  
  2842.     // if stationary, use untitled and close down the files
  2843.     if (pData->originalFileType == 'sEXT')
  2844.         {
  2845.         pData->originalFileType = 'TEXT';
  2846.         pData->openAsNew = true;
  2847.         if (pData->resRefNum != -1)
  2848.             CloseResFile(pData->resRefNum);
  2849.         if (pData->dataRefNum != -1)
  2850.             FSClose(pData->dataRefNum);
  2851.         pData->resRefNum = pData->dataRefNum = -1;
  2852.         }
  2853.         
  2854.     // initalize undo information
  2855.     ((TextDataPtr) pData)->prevCommandID = cNull;
  2856.     
  2857.     // if we have voices, add them to the menu
  2858.     if ( (gMachineInfo.haveTTS) && (!gAddedVoices) )
  2859.         {
  2860.         short    theVoiceCount;
  2861.         short    i, item;
  2862.         
  2863.         if (CountVoices( &theVoiceCount ) == noErr)
  2864.             {
  2865.             VoiceSpec            spec;                // A voice to add to the menu.
  2866.             VoiceDescription    description;        // Info about a voice.
  2867.             MenuHandle            voicesMenu = GetMenuHandle(mVoices);
  2868.  
  2869.             anErr = GetVoiceDescription( nil, &description, sizeof(description) );
  2870.             if (anErr == noErr)
  2871.                 {
  2872.                 gCurrentVoice = description.voice;
  2873.                 for (i = 1; i <= theVoiceCount; ++i)
  2874.                     {
  2875.                     if ( (GetIndVoice( i, &spec ) == noErr)  &&
  2876.                          (GetVoiceDescription( &spec, &description, sizeof(description) ) == noErr ) )
  2877.                         {
  2878.                         short    menuCount = CountMItems( voicesMenu );
  2879.                         
  2880.                         // first one we are adding == get rid of item already there
  2881.                         if ( (i == 1)  && (menuCount > 0) )
  2882.                             {
  2883.                             DeleteMenuItem( voicesMenu, 1 );
  2884.                             --menuCount;
  2885.                             }
  2886.                             
  2887.                         for ( item = 1; item <= menuCount; ++item )
  2888.                             {
  2889.                             Str255    itemText;
  2890.                             
  2891.                             GetMenuItemText( voicesMenu, item, itemText );
  2892.                             /*1st > 2nd*/
  2893.                             if ( IUCompString( itemText, description.name ) == 1 )
  2894.                                 break;                        // Found where name goes in list.
  2895.                             }
  2896.         
  2897.                         InsertMenuItem( voicesMenu, "\p ", item - 1 );
  2898.                         SetMenuItemText( voicesMenu, item, description.name );
  2899.                         
  2900.                         CheckItem(voicesMenu, item, 
  2901.                             ((gCurrentVoice.creator == spec.creator) && (gCurrentVoice.id == spec.id)) );
  2902.                         }
  2903.                     }
  2904.     
  2905.                 }
  2906.             
  2907.             gAddedVoices = true;
  2908.             }
  2909.             
  2910.         } // end of adding voices
  2911.         
  2912.     return noErr;
  2913.     
  2914. // EXCEPTION HANDLING
  2915. ReadData:
  2916.     TEDispose(((TextDataPtr) pData)->hTE);
  2917.     
  2918. TENewFailed:
  2919.  
  2920.     return anErr;
  2921.     
  2922. } // TextMakeWindow
  2923.  
  2924.  
  2925. // --------------------------------------------------------------------------------------------------------------
  2926.  
  2927. OSErr    TextPreflightWindow(PreflightPtr pPreflightData)
  2928. {    
  2929.     pPreflightData->continueWithOpen     = true;
  2930.     pPreflightData->wantVScroll            = true;
  2931.     pPreflightData->doZoom                = true;
  2932.     pPreflightData->makeProcPtr         = TextMakeWindow;
  2933.     if (pPreflightData->fileType != 'ttro')
  2934.         pPreflightData->openKind            = fsRdWrPerm;
  2935.     
  2936.     pPreflightData->storageSize         = sizeof(TextDataRecord);
  2937.  
  2938.     // get strings that mark the picture
  2939.     GetIndString(gPictMarker1, kTextStrings, iPictureMarker1);
  2940.     GetIndString(gPictMarker2, kTextStrings, iPictureMarker2);
  2941.     
  2942.     return noErr;
  2943.     
  2944. } // TextPreflightWindow
  2945.  
  2946. // --------------------------------------------------------------------------------------------------------------
  2947.  
  2948. void TextGetFileTypes(OSType * pFileTypes, OSType * pDocumentTypes, short * numTypes)
  2949. {
  2950.     pFileTypes[*numTypes]         = 'TEXT';
  2951.     pDocumentTypes[*numTypes]     = kTextWindow;
  2952.     (*numTypes)++;
  2953.         
  2954.     pFileTypes[*numTypes]         = 'ttro';
  2955.     pDocumentTypes[*numTypes]     = kTextWindow;
  2956.     (*numTypes)++;
  2957.         
  2958.     pFileTypes[*numTypes]         = 'sEXT';
  2959.     pDocumentTypes[*numTypes]     = kTextWindow;
  2960.     (*numTypes)++;
  2961.         
  2962. } // TextGetFileTypes
  2963.  
  2964. // --------------------------------------------------------------------------------------------------------------
  2965.  
  2966.  
  2967.  
  2968. // TextAddContentsMenu checks if there is a contents list and, if there
  2969. // is, creates a new menu handle for the contents list and fills it with 
  2970. // the appropriate visible items
  2971.  
  2972. void TextAddContentsMenu(WindowDataPtr pData)
  2973. {
  2974.     MenuHandle    contentsMenu;
  2975.     Str255        menuStr;
  2976.     short        totalItems;
  2977.     short        index;
  2978.     OSErr        err;
  2979.     
  2980.     contentsMenu = GetMenuHandle(mContents);
  2981.     require(contentsMenu == nil, ContentsMenuAlreadyInstalled);
  2982.     
  2983.     // Is there a contents list?  If so, get the menu name 
  2984.     // and the number of items in the list
  2985.     
  2986.     if (TextGetContentsListItem(pData, 0, menuStr, nil, &totalItems) == noErr)
  2987.         {
  2988.         
  2989.         // create the menu and fill it with all the items
  2990.         // listed in the string list resource
  2991.         
  2992.         contentsMenu = NewMenu(mContents, menuStr);
  2993.         require(contentsMenu != nil, CantCreateContentsMenu);
  2994.         
  2995.         for (index = 1; index < totalItems; index++)
  2996.             {
  2997.             err = TextGetContentsListItem(pData, index, menuStr, nil, nil);
  2998.             require(err == noErr, CantGetItem);
  2999.             
  3000.             AppendMenu(contentsMenu, menuStr);
  3001.             }
  3002.  
  3003.         // add the menu to the menu bar, and redraw the menu bar
  3004.         InsertMenu(contentsMenu, 0);
  3005.         DrawMenuBar();
  3006.         }
  3007.     else
  3008.         {
  3009.         // no contents, do nothing
  3010.         }
  3011.     
  3012.     return;
  3013.     
  3014. // error handling
  3015. CantGetItem:
  3016. CantCreateContentsMenu:
  3017.     
  3018.     if (contentsMenu)     DisposeMenu(contentsMenu);
  3019.  
  3020. ContentsMenuAlreadyInstalled:
  3021.  
  3022.     return;
  3023.         
  3024. } // TextAddContentsMenu
  3025.  
  3026.  
  3027.  
  3028.  
  3029. // TextRemoveContentsMenu removes the contents menu, if any,
  3030. // and redraws the menu bar
  3031.  
  3032. static void TextRemoveContentsMenu(WindowDataPtr pData)
  3033. {
  3034. #pragma unused (pData)
  3035.  
  3036.     MenuHandle    contentsMenu;
  3037.     
  3038.     contentsMenu = GetMenuHandle(mContents);
  3039.     if (contentsMenu)
  3040.         {
  3041.         DeleteMenu(mContents);
  3042.         DisposeMenu(contentsMenu);
  3043.         DrawMenuBar();
  3044.         }
  3045.  
  3046. } // TextRemoveContentsMenu
  3047.  
  3048.  
  3049.  
  3050. // TextGetContentsListItem is a general utility routine for examining the
  3051. // contents menu list, returning the menu and search strings, and returning
  3052. // the total number of entries in the contents list.
  3053. //
  3054. // Pass 0 as itemNum to retrieve the strings for the contents menu title.
  3055. //
  3056. // Pass nil for menuStr, searchStr, or totalItems if you don't want that
  3057. // info returned.
  3058. //
  3059. // Returns eDocumentHasNoContentsEntries if there is no contents string list
  3060. // resource for the specified window
  3061.  
  3062. static OSErr TextGetContentsListItem(WindowDataPtr pData, short itemNum, 
  3063.                               StringPtr menuStr, StringPtr searchStr, 
  3064.                               short *totalItems)
  3065. {
  3066.  
  3067.     OSErr    err;
  3068.     short    oldResFile;
  3069.     short    menuItemNum;
  3070.     short    searchItemNum;
  3071.     Handle    contentsStrListHandle;
  3072.     
  3073.     // if no original resource file, don't bother
  3074.     if (pData->resRefNum == -1)    
  3075.         {
  3076.         return eDocumentHasNoContentsEntries;
  3077.         }
  3078.         
  3079.     err = noErr;
  3080.     
  3081.     oldResFile = CurResFile();
  3082.     UseResFile(pData->resRefNum);
  3083.     
  3084.     // two entries per item
  3085.     //
  3086.     // first (itemNum zero) is content menu title
  3087.     // (second -- itemNum one, search string for menu title -- is unused)
  3088.     
  3089.     menuItemNum = itemNum * 2 + 1;
  3090.     searchItemNum = menuItemNum + 1;
  3091.     
  3092.     contentsStrListHandle = Get1Resource('STR#', kContentsListID);
  3093.     if (contentsStrListHandle)
  3094.         {
  3095.         if (totalItems)    *totalItems = (*(short *)*contentsStrListHandle) / 2;
  3096.  
  3097.         if (menuStr)    GetIndString(menuStr, kContentsListID, menuItemNum);
  3098.         if (searchStr)    
  3099.             {
  3100.             GetIndString(searchStr, kContentsListID, searchItemNum);
  3101.         
  3102.             if (searchStr[0] == 0)
  3103.                 {
  3104.                 // search string was empty, so use the
  3105.                 // menu string as the search string
  3106.                 
  3107.                 GetIndString(searchStr, kContentsListID, menuItemNum);
  3108.                 }
  3109.             }
  3110.         }
  3111.     else
  3112.         {
  3113.         err = eDocumentHasNoContentsEntries;
  3114.         if (totalItems)    *totalItems = 0;
  3115.         }
  3116.     
  3117.     UseResFile(oldResFile);
  3118.     
  3119.     return err;
  3120. } // TextGetContentsListItem
  3121.  
  3122.  
  3123. // TextAdjustContentsMenu enables the items in the contents menu
  3124. //
  3125. // This routine is essentially a placeholder in case the contents
  3126. // menu really were to be dynamically enabled.
  3127.  
  3128. static OSErr TextAdjustContentsMenu(WindowDataPtr pData)
  3129. {
  3130. #pragma unused (pData)
  3131.     
  3132.     EnableCommand(cSelectContents);
  3133.     return(noErr);
  3134.     
  3135. } // TextAdjustContentsMenu
  3136.  
  3137.